gdb: break in shared library loaded by python - python

I'm trying to debug c/c++ code located in shared libraries which are loaded by ctypes.cdll.LoadLibrary() in python and then specific functions are called from python.
The python code forks child processes, so I need to be able to break whether the c function is called from a python parent or child process.
A dead-simple example: test.c
// j = clib.call1(i)
int call1(int i)
{
return i*2;
}
test.py
import os, sys, ctypes
path = os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]), "test.so"))
clib = ctypes.cdll.LoadLibrary(path)
i = 20
j = clib.call1(i)
print "i=%d j=%d\n" %(i, j)
$ gcc -g -O0 test.c -shared -o test.so
$ gdb --args python-dbg test.py
(gdb) break test.c call1
Function "test.c call1" not defined.
Make breakpoint pending on future shared library load? (y or [n]) y
Breakpoint 1 (test.c call1) pending.
(gdb) info breakpoints
Num Type Disp Enb Address What
1 breakpoint keep y <PENDING> test.c call1
(gdb) run
Starting program: /usr/bin/python-dbg test.py
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
i=20 j=40
[23744 refs]
[Inferior 1 (process 44079) exited normally]
You can see from my terminal log that gdb is not seeing the breakpoint when python loads the library. I am seeing the same behavior with my application.

break on call1 instead
(gdb) break call1
this should work too
(gdb) break test.c:call1

Related

How to use cuda-gdb python debugging?

I write a hello.py that is simply
print("hi")
and then run
cuda-gdb python3 hello.py
I get:
Reading symbols from python3...
(No debugging symbols found in python3)
"/home/x/Desktop/py_projects/hello.py" is not a core dump: file format not recognized
How to debug if I call cuda functions in python code?
Its possible to use cuda-gdb from python, assuming you only need to debug the C/C++ portion. I don't know of a debugger that can jump from debugging python to debugging CUDA C++. Here is one possible approach, a copy of what is presented here.
To debug a CUDA C/C++ library function called from python, the following is one possibility, inspired from this article.
For this walk through, I will use the t383.py and t383.cu files verbatim from this answer, and I'll be using CUDA 10, python 2.7.5, on CentOS7
Compile your CUDA C/C++ library using the -G and -g switches, as you would to do ordinary debug:
$ nvcc -Xcompiler -fPIC -std=c++11 -shared -arch=sm_60 -G -g -o t383.so t383.cu -DFIX
We'll need two terminal sessions for this. I will refer to them as session 1 and session 2. In session 1, start your python interpreter:
$ python
...
>>>
In session 2, find the process ID associated with your python interpreter (replace USER with your actual username):
$ ps -ef |grep USER
...
USER 23221 22694 0 23:55 pts/0 00:00:00 python
...
$
In the above example, 23221 is the process ID for the python interpreter (use man ps for help)
In session 2, start cuda-gdb so as to attach to that process ID:
$ cuda-gdb -p 23221
... (lots of spew here)
(cuda-gdb)
In session 2, at the (cuda-gdb) prompt, set a breakpoint at a desired location in your CUDA C/C++ library. For this example, we will set a breakpoint at one of the first lines of kernel code, line 70 in the t383.cu file. If you haven't yet loaded the library (we haven't, in this walk through), then cuda-gdb will point this out and ask you if you want to make the breakpoint pending on a future library load. Answer y to this (alternatively, before starting this cuda-gdb session, you could have run your python script once from within the interpreter, as we will do in step 7 below. This would load the symbol table for the library and avoid this prompt). After the breakpoint is set, we will issue the continue command in cuda-gdb in order to get the python interpreter running again:
(cuda-gdb) break t383.cu:70
No symbol table is loaded. Use the "file" command.
Make breakpoint pending on future shared library load? (y or [n]) y
Breakpoint 1 (t383.cu:70) pending.
(cuda-gdb) continue
Continuing.
In session 1, run your python script:
>>> execfile("t383.py")
init terrain_height_map
1, 1, 1, 1, 1,
1, 1, 1, 1,
1, 1, 1, 1,
our python interpreter has now halted (and is unresponsive), because in session 2 we see that the breakpoint has been hit:
[New Thread 0x7fdb0ffff700 (LWP 23589)]
[New Thread 0x7fdb0f7fe700 (LWP 23590)]
[Switching focus to CUDA kernel 0, grid 1, block (0,0,0), thread (0,0,0), device 0, sm 0, warp 0, lane 0]
Thread 1 "python" hit Breakpoint 1, update_water_flow<<<(1,1,1),(1024,1,1)>>> (
water_height_map=0x80080000800000, water_flow_map=0xfffcc000800600,
d_updated_water_flow_map=0x7fdb00800800, SIZE_X=4, SIZE_Y=4) at t383.cu:70
70 int col = index % SIZE_X;
(cuda-gdb)
and we see that the breakpoint is at line 70 of our library (kernel) code, just as expected. ordinary C/C++ cuda-gdb debug can proceed at this point within session 2, as long as you stay within the library function.
When you are finished debugging (you may need to remove any breakpoints set) you can once again type continue in session 2, to allow control to return to the python interpreter in session 1, and for your application to finish.
To complete Robert's answer, if you are using CUDA-Python, you can use option --args in order to pass a command-line that contains arguments. For example, this is a valid command-line:
$ cuda-gdb --args python3 hello.py
Your original command is not valid because, without --args, cuda-gdb takes in parameter a host coredump file.
Here is the complete command line with an example from the CUDA-Python repository:
$ cuda-gdb -q --args python3 simpleCubemapTexture_test.py
Reading symbols from python3...
(No debugging symbols found in python3)
(cuda-gdb) set cuda break_on_launch application
(cuda-gdb) run
Starting program: /usr/bin/python3 simpleCubemapTexture_test.py.
...
[Switching focus to CUDA kernel 0, grid 1, block (0,0,0), thread (0,0,0), device 0, sm 0, warp 0, lane 0]
0x00007fff67858600 in transformKernel<<<(8,8,1),(8,8,1)>>> ()
(cuda-gdb) p $pc
$1 = (void (*)()) 0x7fff67858600 <transformKernel>
(cuda-gdb) bt
#0 0x00007fff67858600 in transformKernel<<<(8,8,1),(8,8,1)>>> ()

SWIG: read/write same variable from python and C++

I am not really sure if I'm taking the right path here.
(See Edit below)
I have a C++ library called myapp that compiles into libmyapp.so. This library contains a file called swigtest.h.
The thing I want to do, is wrapping this file with SWIG to create an interface for python and using this interface to change or read specific values from myapp.
Let's say myapp is a console application that has some global variables like static int myGlobalVar = 5.
In myapp I have a function to read and to write the global variable:
int ReadGlobalVar() { return myGlobalVar; }
void SetGlobalVar(int value) { myGlobalVar = value; }
Here are my steps for compiling the module:
swig -c++ -python swigtest.i
g++ -fpic -c swigtest.cpp swigtest_wrap.cxx -L/pathToMyApp -lmyapp -I/usr/include/python3.6
g++ -Wall -Wextra -shared swigtest.o swigtest_wrap.o -o _swigtest.so -L/pathToMyApp -lmyapp
I open and load the python module as following:
LD_LIBRARY_PATH=/pathToMyApp python3
import swigtest
...
The problem I face know is, that python and my application are still separated from one another. If i modify the variable using the python module, it does not affect the c++ application and vice versa.
Is it even possible to link those two together or is SWIG only used to reuse C++ code in python?
I think the big problem here is that I have only a small understanding of how libraries are linked and created under C++.
I found a few tutorials on using SWIG with Python for example:http://books.gigatux.nl/mirror/pythonprogramming/0596000855_python2-CHP-19-SECT-8.html
and https://www.geeksforgeeks.org/wrapping-cc-python-using-swig-set-1/
===================
Edit:
I created another example application for this scenario:
1) files:
main.cpp
#include <stdio.h>
#include <unistd.h>
#include <iostream>
#include "Swigtest.h"
using namespace std;
int main()
{
Swigtest swigtest;
for(;;)
{
sleep(1);
cout << "Global Variable value:" << swigtest.ReadGlobalVar() << endl;
}
}
Swigtest.h
#ifndef SWIGTEST_H
#define SWIGTEST_H
static int myGlobalVar = 5;
class Swigtest
{
public:
Swigtest();
void SetGlobalVar(int var){ myGlobalVar = var; }
int ReadGlobalVar() { return myGlobalVar; }
};
#endif // SWIGTEST_H
Swigtest.cpp
#include "Swigtest.h"
Swigtest::Swigtest()
{
}
Swigtest.i
%module swigtest
%{
#include "Swigtest.h"
%}
%include "Swigtest.h"
2) compile application
g++ -fpic -c Swigtest.cpp
g++ -fpic -c main.cpp
g++ Swigtest.o main.o -o libmyapp.so
3) compile python module
swig -c++ -python Swigtest.i
g++ -fpic -c Swigtest.cpp Swigtest_wrap.cxx -L. -lmyapp -I/usr/include/python3.6
g++ -Wall -Wextra -shared Swigtest.o Swigtest_wrap.o -o _swigtest.so -L. -lmyapp
now I have my application in myapp.so and the python extension in _swigtest.so
4) executing test
executing application
I launch the application by executing it from the shell:
./libmyapp.so
Output:
Global Variable value:5
Global Variable value:5
Global Variable value:5
Global Variable value:5
Global Variable value:5
Global Variable value:5
Global Variable value:5
Global Variable value:5
Global Variable value:5
Global Variable value:5
Global Variable value:5
Global Variable value:5
Global Variable value:5
Global Variable value:5
Global Variable value:5
testing python module
in another terminal i open python (at the same path where libmyapp.sois placed) and import the module
LD_LIBRARY_PATH=. python3
output:
Python 3.6.9 (default, Nov 7 2019, 10:44:02)
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import swigtest
>>> a=swigtest.Swigtest()
>>> a.ReadGlobalVar()
5
>>> a.SetGlobalVar(3)
>>> a.ReadGlobalVar()
3
>>> a.ReadGlobalVar()
3
>>>
in the meantime the c++ application I launched from the terminal is happily outputting its value of 5.
What do I want?
I want that the change in the value also affects the c++ application. Above I changed the value from 5 to 3 in the python module, but the c++ application was unaffected from it.
Is this possible with SWIG? or am I doing something wrong here.
In Python it changes because it changes in C++. But it changes the C++ value inside the Python process. Each process run in a completely separated context. So when you launch the C++ process separately it runs in completely other context, having its own set of variables, memory, registries. No matter what you do in a process will not affect the other process. No matter how many processes you launch, none will affect the content of the other. In order to send data from one process to another you need a way to communicate via Inter Process Communication (IPC). You send data into via files, sockets, pipes, RPC, messages.

How to redirect STDOUT of a program to STDIN of a GDB-debugged program?

I normally redirect STDOUT to another program using:
python -c 'print("HelloWorld")' | ./myprog
I know that I can supply the contents of a file as STDIN for a debugged program in GDB:
(gdb) run myprog < input.txt
However, how can I do something like:
(gdb) run mypprog < python -c 'print("HelloWorld")'
without first having to create a file with the output of python -c '...'?
One way is to attach gdb to your already-running process. Find its pid with ps or top. Let's say that it's 37. Then run
(gdb) attach 37
That probably won't work for your case with very short run time though. Another approach is to use a fifo.
mkfifo fifo
python -c 'print("Hello World")' > fifo &
gdb myprog
run < fifo

Import Error: No module name libstdcxx

When I use gdb to debug my C++ program with segmentation fault, I come with this error in gdb.
Traceback (most recent call last):
File "/usr/share/gdb/auto-load/usr/lib/x86_64-linux- gnu/libstdc++.so.6.0.19-gdb.py", line 63, in
from libstdcxx.v6.printers import register_libstdcxx_printers
ImportError: No module named 'libstdcxx'
I am using Gdb 7.7.1 and g++ version 4.8.4. I have googled around but haven't get answers. Can any one solve my error? Thank you very much.
This is a bug in /usr/lib/debug/usr/lib/$triple/libstdc++.so.6.0.18-gdb.py;
When you start gdb, please enter:
python sys.path.append("/usr/share/gcc-4.8/python");
I encountered this error during using gdb in emacs. (in docker container - ubuntu)
I tried it like below and worked well.
(1) open libstdc++.so.x.x.x-gdb.py
sh> sudo vi /usr/share/gdb/auto-load/usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.19-gdb.py
(2) modify that file(libstdc++.so.x.x.x-gdb.py
) like below.
import sys
import gdb
import os
import os.path
pythondir = '/usr/share/gcc-4.8/python'
libdir = '/usr/lib/x86_64-linux-gnu'
sys.path.append(pythondir) <-- add this code
(3) execute gdb again
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./a.out...done.
(gdb) b main
Breakpoint 1 at 0x400ae9: file vector7.cpp, line 7.
(gdb) r
Starting program: /home/dplee/work/study_room/c++/a.out
Breakpoint 1, main () at vector7.cpp:7
7 vector<int> v(10);
(gdb) list
2 #include <vector>
3 using namespace std;
4
5 int main()
6 {
7 vector<int> v(10);
8 int num = 0;
9
10 for(auto& i : v)
11 {
(gdb)
I picked libstdcxx from gcc installation path, and this error went away for me.
sys.path.insert(0, '/global/freeware/Linux/RHEL6/gcc-6.2.0/share/gcc-6.2.0/python')
If you used sudo to start the gdb, make sure you have the PATH correct.
Try this sudo PATH=$PATH gdb ...
For those who do not have sudo privilege, setting PYTHONPATH env before running gdb will work:
export PYTHONPATH="/usr/share/gcc-<your_version>/python:${PYTHONPATH}"

Programatically testing for openmp support from a python setup script

I'm working on a python project that uses cython and c to speed up time sensitive operations. In a few of our cython routines, we use openmp to further speed up an operation if idle cores are available.
This leads to a bit of an annoying situation on OS X since the default compiler for recent OS versions (llvm/clang on 10.7 and 10.8) doesn't support openmp. Our stopgap solution is to tell people to set gcc as their compiler when they build. We'd really like to do this programmatically since clang can build everything else with no issues.
Right now, compilation will fail with the following error:
clang: error: linker command failed with exit code 1 (use -v to see invocation)
error: Command "cc -bundle -undefined dynamic_lookup -L/usr/local/lib -L/usr/local/opt/sqlite/lib build/temp.macosx-10.8-x86_64-2.7/yt/utilities/lib/geometry_utils.o -lm -o yt/utilities/lib/geometry_utils.so -fopenmp" failed with exit status 1
The relevant portion of our setup script looks like this:
config.add_extension("geometry_utils",
["yt/utilities/lib/geometry_utils.pyx"],
extra_compile_args=['-fopenmp'],
extra_link_args=['-fopenmp'],
libraries=["m"], depends=["yt/utilities/lib/fp_utils.pxd"])
The full setup.py file is here.
Is there a way to programmatically test for openmp support from inside the setup script?
I was able to get this working by checking to see if a test program compiles:
import os, tempfile, subprocess, shutil
# see http://openmp.org/wp/openmp-compilers/
omp_test = \
r"""
#include <omp.h>
#include <stdio.h>
int main() {
#pragma omp parallel
printf("Hello from thread %d, nthreads %d\n", omp_get_thread_num(), omp_get_num_threads());
}
"""
def check_for_openmp():
tmpdir = tempfile.mkdtemp()
curdir = os.getcwd()
os.chdir(tmpdir)
filename = r'test.c'
with open(filename, 'w', 0) as file:
file.write(omp_test)
with open(os.devnull, 'w') as fnull:
result = subprocess.call(['cc', '-fopenmp', filename],
stdout=fnull, stderr=fnull)
os.chdir(curdir)
#clean up
shutil.rmtree(tmpdir)
return result

Categories