Python 子进程(调用 gdb)在 make 调用时冻结?

问题描述 投票:0回答:1

好吧,这是一个棘手的问题。

我有一个 CMake 项目来构建可执行文件,然后我有一个单独的独立“目标”,它允许我运行一个 Python 脚本,该脚本使用子进程来调用

gdb
并检查此可执行文件。请注意,我在 Windows 10 上的 MINGW64 (MSYS2)
bash
shell 中运行所有这些。

生成示例文件的脚本作为

gen_files.sh
发布在帖子末尾;要生成示例,请运行以下
bash
命令:

mkdir C:/tmp/cmake_py_gdb
cd C:/tmp/cmake_py_gdb
# save the file as gen_files.sh there
bash gen_files.sh
mkdir build
cd build
cmake ../ -DCMAKE_BUILD_TYPE=Debug -G "MSYS Makefiles"
make
./myprogram.exe
# to trigger the freeze:
make py_gdb_inspect

这里是棘手的部分:

make py_gdb_inspect
本质上调用了
python3 py_gdb_inspect.py
;但是,如果我直接从 MINGW64
python3 py_gdb_inspect.py
shell 调用
bash
,一切都会按预期工作:

$ (cd C:/tmp/cmake_py_gdb; python3 py_gdb_inspect.py)
> Working directory C:\tmp\cmake_py_gdb.
> Working directory C:\tmp\cmake_py_gdb\build.
> Before start_engine...<function start_engine at 0x000001bb42f94ae0>
> start_engine
> Starting debug engine/process: "myprogram.exe"
> resp1:
> resp2: Breakpoint 1 at 0x1400014d3: file C:/tmp/cmake_py_gdb/myprogram.c, line 11.
> [New Thread 20724.0x3c1c]
> mynumber_val: -1
>
> [... snip empty lines ...]
>
py_gdb_inspect.py finished!

但是,当我调用

make py_gdb_inspect
时,脚本冻结在这里:

$ make py_gdb_inspect
make[1]: Entering directory '/c/tmp/cmake_py_gdb/build'
make[2]: Entering directory '/c/tmp/cmake_py_gdb/build'
make[3]: Entering directory '/c/tmp/cmake_py_gdb/build'
make[3]: Leaving directory '/c/tmp/cmake_py_gdb/build'
make[3]: Entering directory '/c/tmp/cmake_py_gdb/build'
[100%] run py_gdb_inspect.py in C:/tmp/cmake_py_gdb
> Working directory C:\tmp\cmake_py_gdb.
> Working directory C:\tmp\cmake_py_gdb\build.
> Before start_engine...<function start_engine at 0x0000011da8014ae0>
> start_engine
> Starting debug engine/process: "myprogram.exe"
> resp1:
> resp2: Breakpoint 1 at 0x1400014d3: file C:/tmp/cmake_py_gdb/myprogram.c, line 11.

...我什至无法用 CTRL+C 来阻止它,我必须使用

pkill -9 -f python
make
也会输出:

make[3]: *** [CMakeFiles/py_gdb_inspect.dir/build.make:58: CMakeFiles/py_gdb_inspect] Killed
make[3]: Leaving directory '/c/tmp/cmake_py_gdb/build'
make[2]: *** [CMakeFiles/Makefile2:110: CMakeFiles/py_gdb_inspect.dir/all] Error 2
make[2]: Leaving directory '/c/tmp/cmake_py_gdb/build'
make[1]: *** [CMakeFiles/Makefile2:117: CMakeFiles/py_gdb_inspect.dir/rule] Error 2
make[1]: Leaving directory '/c/tmp/cmake_py_gdb/build'
make: *** [Makefile:131: py_gdb_inspect] Error 2

我的猜测是,可能当

gdb
启动“[新线程20724.0x3c1c]”时,终端的stdout/stderr文件描述符会变得混乱——特别是因为这里我们有一个调用链: -> python (MINGW64 的那个) -> gdb -> python (GDB 内置的那个) [1]

不幸的是,我无法弄清楚究竟发生了什么 - 所以我的问题是:如何强制

python3 py_gdb_inspect.py
命令完成,而不是冻结,以及通过
make py_gdb_inspect
调用它时?

gen_files.sh

read -r -d '' README <<'EOF'
To run this example:

mkdir C:/tmp/cmake_py_gdb
cd C:/tmp/cmake_py_gdb
# save this file as gen_files.sh there
bash gen_files.sh
mkdir build
cd build
cmake ../ -DCMAKE_BUILD_TYPE=Debug -G "MSYS Makefiles"
make
./myprogram.exe
# to trigger the freeze:
make py_gdb_inspect
EOF

cat > myprogram.c <<EOF
#include <stdio.h>

int add_two_vars(int a, int b) {
  int result = a + b;
  return result;
}

int mynumber = -1;

int main() {
  int a = 5;
  int b = 2;
  mynumber = add_two_vars(a, b);
  printf("Hello from myprogram.exe! mynumber is %d\n", mynumber);
  return 0;
}
EOF

cat > CMakeLists.txt <<'EOF'
cmake_minimum_required(VERSION 3.13)

find_package(Python3 COMPONENTS Interpreter)
message(STATUS
    "Python: version=${Python3_VERSION} interpreter=${Python3_EXECUTABLE}")
if(NOT Python3_FOUND)
    #   find_package() will not abort the build if anything's missing.
    string(JOIN "\n" errmsg
        "  Python3 not found."
        "  - Python3_FOUND=${Python3_FOUND}"
        )
    message(FATAL_ERROR ${errmsg})
endif()

project(myprogram C)
set(CMAKE_C_STANDARD 11)
set(${PROJECT_NAME}_sources
    myprogram.c
)

add_executable(${PROJECT_NAME} ${${PROJECT_NAME}_sources})

add_custom_target(py_gdb_inspect
  ${Python3_EXECUTABLE} py_gdb_inspect.py
  WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
  COMMENT "run py_gdb_inspect.py in ${CMAKE_CURRENT_SOURCE_DIR}"
)
EOF

cat > py_gdb_inspect.py <<'EOF'
import sys,os
import subprocess

GDB = "D:/msys64/mingw64/bin/gdb-multiarch.exe"
# GDB scripts are known as "GDB command files";
# extension ".gdb" for "GDB's own command language" 
GDB_TEST_SCRIPT = "test_cmdfile.gdb"
GDB_TEST_CMD  = [ GDB, "-q", "-batch", "-x", GDB_TEST_SCRIPT ]

try:
  result = subprocess.Popen(GDB_TEST_CMD, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True, cwd=os.getcwd(), bufsize=1, close_fds=True )
except Exception as ex:
  print(ex)
  sys.exit(1)

while result.poll() is None: # for line in iter(result.stdout.readline, b''):
  tline = result.stdout.readline().strip()
  print("> {}".format(tline))

print("py_gdb_inspect.py finished!")
EOF

cat > test_cmdfile.gdb <<'EOF'
python
import time
def start_engine(in_exe, printresp=False):
  print('start_engine')
  print('Starting debug engine/process: "{}"'.format(in_exe))
  response_1 = gdb.execute('file {}'.format(in_exe), to_string=True)
  if printresp: print("resp1: " + response_1.strip())
  # silent breakpoint (avoid printing "Thread 1 hit Breakpoint 1"): https://stackoverflow.com/q/58528800
  response_2 = gdb.execute("b main\ncommands\nsilent\nend", to_string=True)
  if printresp: print("resp2: " + response_2.strip())
  # FOR SOME REASON, now when we run gdb.execute('r') when script is called from make, it BLOCKS! from_tty=True does not help
  #time.sleep(1)
  #response_3 = gdb.execute('r', to_string=True, from_tty=True)
  #if printresp: print("resp3: " + response_3.strip())
def print_value():
  mynumber_val = gdb.execute('printf "%d", mynumber', to_string=True)
  print("mynumber_val: {}".format(mynumber_val))
end

pwd
cd build
pwd
python print("Before start_engine...{}".format(start_engine))
python start_engine("myprogram.exe", printresp=True)
#tty gdb_tty.txt
run
py print_value()
EOF

[1] 抱歉,我忍不住指出了这样一个梗:“哟,老兄,我喜欢 Python,所以我在你的 gdb 中放了一个 Python,这样你就可以在使用 Python 时使用 Python。”

:)

python subprocess gdb
1个回答
0
投票

找到它 - 解决方案是将

creationflags
subprocess.CREATE_NEW_CONSOLE
subprocess.DETACHED_PROCESS
一起使用 - 不幸的是,在这两种设置中,都会短暂显示
cmd.exe
窗口,并且似乎不可能隐藏它,这令人恼火 - 然而,更烦人的冻结问题解决了!

  result = subprocess.Popen(GDB_TEST_CMD, \
    stdout=subprocess.PIPE, \
    stderr=subprocess.STDOUT, \
    universal_newlines=True, \
    cwd=os.getcwd(), \
    bufsize=1, \
    close_fds=True, \
    creationflags = ( subprocess.CREATE_NEW_CONSOLE | subprocess.SW_HIDE | subprocess.CREATE_NO_WINDOW ) # SW_HIDE and CREATE_NO_WINDOW still show window; either DETACHED_PROCESS or CREATE_NEW_CONSOLE works (CREATE_BREAKAWAY_FROM_JOB, CREATE_NEW_PROCESS_GROUP does not work, still freezes then) \
)
© www.soinside.com 2019 - 2024. All rights reserved.