Skip to content

Commit

Permalink
refactor: libaustin
Browse files Browse the repository at this point in the history
  • Loading branch information
P403n1x87 committed Oct 20, 2022
1 parent 74efe06 commit 5b7b1e2
Show file tree
Hide file tree
Showing 25 changed files with 991 additions and 266 deletions.
6 changes: 5 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ jobs:
pushd src
tar -Jcf austin-$VERSION-gnu-linux-amd64.tar.xz austin
tar -Jcf austinp-$VERSION-gnu-linux-amd64.tar.xz austinp
cp ../include/libaustin.h .
cp .libs/libaustin.a .
tar -Jcf libaustin-$VERSION-gnu-linux-amd64.tar.xz libaustin.*
popd
# Build with musl
Expand All @@ -40,7 +44,7 @@ jobs:
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: src/austin-*.tar.xz
file: src/*austin-*.tar.xz
tag: ${{ github.ref }}
overwrite: true
file_glob: true
Expand Down
7 changes: 6 additions & 1 deletion .github/workflows/release_arch.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,14 @@ jobs:
musl-gcc -O3 -Os -s -Wall -pthread *.c -o austin -D__MUSL__
tar -Jcf austin-$VERSION-musl-linux-${{ matrix.arch }}.tar.xz austin
cp ../include/libaustin.h .
cp .libs/libaustin.a .
tar -Jcf libaustin-$VERSION-gnu-linux-${{ matrix.arch }}.tar.xz libaustin.*
mv austin-$VERSION-gnu-linux-${{ matrix.arch }}.tar.xz /artifacts
mv austinp-$VERSION-gnu-linux-${{ matrix.arch }}.tar.xz /artifacts
mv austin-$VERSION-musl-linux-${{ matrix.arch }}.tar.xz /artifacts
mv libaustin-$VERSION-gnu-linux-${{ matrix.arch }}.tar.xz /artifacts
popd
- name: Show artifacts
Expand All @@ -55,7 +60,7 @@ jobs:
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: artifacts/austin-*.tar.xz
file: artifacts/*austin-*.tar.xz
tag: ${{ github.ref }}
overwrite: true
file_glob: true
4 changes: 2 additions & 2 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ jobs:
steps:
- uses: actions/checkout@v2
- name: Compile Austin
run: gcc -Wall -O3 -g src/*.c -o src/austin
run: gcc -Wall -O3 -g src/*.c -Iinclude -o src/austin

- name: Install Python
uses: actions/setup-python@v4
Expand Down Expand Up @@ -123,7 +123,7 @@ jobs:
- name: Compile Austin
run: |
gcc.exe --version
gcc.exe -O3 -g -o src/austin.exe src/*.c -lpsapi -lntdll -Wall
gcc.exe -O3 -g -o src/austin.exe src/*.c -Iinclude -lpsapi -lntdll -Wall
src\austin.exe --help
- name: Install Python
Expand Down
72 changes: 68 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -276,19 +276,19 @@ git clone --depth=1 https://github.com/P403n1x87/austin.git
On Linux, one can then use the command

~~~ console
gcc -O3 -Os -Wall -pthread src/*.c -o src/austin
gcc -O3 -Os -Wall -pthread src/*.c -Iinclude -o src/austin
~~~

whereas on macOS it is enough to run

~~~ console
gcc -O3 -Os -Wall src/*.c -o src/austin
gcc -O3 -Os -Wall src/*.c -Iinclude -o src/austin
~~~

On Windows, the `-lpsapi -lntdll` switches are needed

~~~ console
gcc -O3 -Os -Wall -lpsapi -lntdll src/*.c -o src/austin
gcc -O3 -Os -Wall -lpsapi -lntdll src/*.c -Iinclude -o src/austin
~~~

Add `-DDEBUG` if you need a more verbose log. This is useful if you encounter a
Expand Down Expand Up @@ -503,7 +503,7 @@ sudo apt install libunwind-dev binutils-dev
and compile with

~~~ console
gcc -O3 -Os -Wall -pthread src/*.c -DAUSTINP -lunwind-ptrace -lunwind-generic -lbfd -o src/austinp
gcc -O3 -Os -Wall -pthread src/*.c -Iinclude -DAUSTINP -lunwind-ptrace -lunwind-generic -lbfd -o src/austinp
~~~

then use as per normal. The extra `-k/--kernel` option is available with
Expand Down Expand Up @@ -545,6 +545,70 @@ show both native and Python frames. Highlighting helps tell frames apart. The
> executable will be available as `austin.p` from the command line.

## libaustin

The code base can also be used to generate a library to embed Austin in your
projects to unwind Python stacks or extract frame information from a running
Python process.

A shared library on Linux can be obtained with

~~~ console
gcc -O3 -Wall -pthread src/*.c -Iinclude -o src/libaustin.so -shared -fPIC -DLIBAUSTIN
~~~

The required headers are in the `include/` subfolder. More information on how
to use the library can be found in the `libaustin.h` header file.

At a glance, this is a typical use. The library needs to be initialised at
runtime before it can be used. Hence, before you call any other of the exported
functions, you must do

~~~ c
#include <libaustin.h>

...

{
...

austin_up();

...
}
~~~

After this call, you can attach any Python process with

~~~ c
pid_t pid = ...;
...
austin_handle_t proc_handle = austin_attach(pid);
~~~

When you want to sample the call stack of an attached process, call

~~~ c
austin_sample(proc_handle, cb);
~~~
where `cb` is a callback function with signature `void ()(pid_t pid, pid_t tid)`
that gets called once a thread stack is ready to be retrieved. To get the frames
from the sampled stack, call `austin_pop_frame()` until it returns `NULL`.
To sample a single thread, use `austin_sample_thread(proc_handle, tid)` instead.
Then retrieve the sampled stack with `austin_pop_frame()` as above.
Once you are done with the process, you should detach it with
~~~ c
austin_detach(proc_handle);
~~~

Once you are done with libaustin entirely, make sure to release resources with
`austin_down()`.


## Logging

Austin uses `syslog` on Linux and macOS, and `%TEMP%\austin.log` on Windows
Expand Down
3 changes: 3 additions & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ AM_INIT_AUTOMAKE
AC_PROG_CC_C99
AC_PROG_CPP

# Use libtool for libaustin.
LT_INIT

# Use the C language and compiler for the following checks
AC_LANG([C])

Expand Down
178 changes: 178 additions & 0 deletions include/libaustin.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
// This file is part of "austin" which is released under GPL.
//
// See file LICENCE or go to http://www.gnu.org/licenses/ for full license
// details.
//
// Austin is a Python frame stack sampler for CPython.
//
// Copyright (c) 2018 Gabriele N. Tornetta <[email protected]>.
// All rights reserved.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.

#pragma once

#ifdef __cplusplus
extern "C" {
#endif

#include <sys/types.h>
#include <stdint.h>


/**
* Austin stack callback.
*
* This function is called back once the unwinding of thread stack has been
* completed, and frames are ready to be retrieved with austin_pop_frame().
* The callback is called with the following arguments:
*
* @param pid_t the PID of the process the thread belongs to.
* @param pid_t the TID of the thread.
*/
typedef void (*austin_callback_t)(pid_t, pid_t);


/**
* Austin process handle.
*
* This is a handle to an attached process that is generally required to perfom
* unwinding operations.
*/
typedef struct _py_proc_t * austin_handle_t;


/**
* The Austin frame structure.
*
* This structure is used to return frame stack information to the user.
*/
typedef struct {
uintptr_t key; // a key that uniquely identifies the frame.
char * filename; // the file name of the source containing the code.
char * scope; // the name of the scope, e.g. the function name
unsigned int line; // the line number.
} austin_frame_t;


/**
* Initialise the Austin library.
*
* This function must be called before any other function in the library.
*/
extern int
austin_up();


/**
* Finalise the Austin library.
*
* This function should be called once the Austin library is no longer needed,
* to free up resources.
*/
extern void
austin_down();


/**
* Attach to a Python process.
*
* This function tries to attach to a running Python process, identified by its
* PID. If the process exists and is a valid Python process, a non-NULL handle
* is returned to the caller, to be used for further operations.
*
* Note that attaching to a Python process is a lightweight operation that does
* not interfere with the execution of the process in any way.
*
* @param pid_t The PID of the process to attach to.
*
* @return a handle to the process, or NULL if the process is not a valid
* Python process.
*/
extern austin_handle_t
austin_attach(pid_t);


/**
* Detach from a Python process.
*
* This function detaches from a Python process, identified by its handle.
*/
extern void
austin_detach(austin_handle_t);


/**
* Sample an attached Python process.
*
* This function samples the call stack of all the threads within the attached
* process. The passed callback function is called after every thread has been
* sampled. Frames are available to be retrieved with austin_pop_frame().
*
* @param austin_handle_t the handle to the process to sample.
* @param austin_callback_t the callback function to call after sampling.
*
* @return 0 if the sampling was successful.
*/
extern int
austin_sample(austin_handle_t, austin_callback_t);


/**
* Sample a single thread.
*
* This function samples the call stack of a single thread within the attached
* process.
*
* @param austin_handle_t the handle to the process to sample.
* @param pid_t the TID of the thread to sample.
*
* @return 0 if the sampling was successful.
*/
extern int
austin_sample_thread(austin_handle_t, pid_t);


/**
* Pop a frame from the stack.
*
* This function pops a frame from the stack of the last sampled thread. This
* function should be called iteratively until it returns NULL, to retrieve all
* the frames in the stack.
*
* @return a valid reference to a frame structure, or NULL otherwise.
*/
extern austin_frame_t *
austin_pop_frame();


/**
* Read a single frame from the attached process.
*
* This function reads a single frame from an attached process, at the given
* remote memory location. This is useful if one is intercepting calls to, e.g.
* _PyEval_EvalFrameDefault and has access to the frame pointer (the second
* argument). This function can then be used to resolve the frame details.
*
* @param austin_handle_t the handle to the process.
* @param void * the remote memory location of the frame.
*
* @return a valid reference to a frame structure, or NULL otherwise.
*/
extern austin_frame_t *
austin_read_frame(austin_handle_t, void *);


#ifdef __cplusplus
}
#endif
10 changes: 9 additions & 1 deletion src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http:#www.gnu.org/licenses/>.

AM_CFLAGS = -I$(srcdir) -Wall -pthread
AM_CFLAGS = -I$(srcdir) -I$(srcdir)/../include -Wall -pthread
OPT_FLAGS = -O3
STRIP_FLAGS = -Os -s

Expand All @@ -39,6 +39,8 @@ man_MANS = austin.1

bin_PROGRAMS = austin

noinst_LTLIBRARIES = libaustin.la

# ---- Austin ----

austin_CFLAGS = $(AM_CFLAGS) $(OPT_FLAGS) $(STRIP_FLAGS) $(COVERAGE_FLAGS) $(DEBUG_OPTS)
Expand All @@ -64,3 +66,9 @@ austinp_SOURCES = $(austin_SOURCES)
austinp_CFLAGS = $(austin_CFLAGS) @AUSTINP_CFLAGS@
austinp_LDADD = @AUSTINP_LDADD@
endif


# ---- libaustin ----

libaustin_la_CFLAGS = $(austin_CFLAGS) -DLIBAUSTIN
libaustin_la_SOURCES = $(austin_SOURCES)
Loading

0 comments on commit 5b7b1e2

Please sign in to comment.