Skip to content

Latest commit

 

History

History
110 lines (81 loc) · 3.95 KB

File metadata and controls

110 lines (81 loc) · 3.95 KB

pymetacg

pymetacg are MetaCG's Python bindings that support parsing MetaCG files from Python projects. Currently, manipulating and writing call graphs is not supported.

Dependencies

Building

pymetacg can be built as an optional feature during the MetaCG build, producing a dynamic library (e.g., pymetacg.cpython-313-x86_64-linux-gnu.so) that can then be loaded from Python. Pass -DMETACG_BUILD_PYMETACG=ON to CMake to enable pymetacg.

$> cmake -S . -B build -DCMAKE_INSTALL_PREFIX=/where/to/install -DMETACG_BUILD_PYMETACG=ON
$> cmake --build build --parallel
$> cmake --install build

By default, nanobind is downloaded automatically during configruation. To use an already installed nanobind, use -DMETACG_USE_EXTERNAL_NANOBIND=ON and specify the installation path using -Dnanobind_ROOT=<your nanobind installation>, if necessary.

The CMake setup of pymetacg uses CMake's FindPython to find the Python installation for which pymetacg will be built. To specify which Python installation to use, use -DPython_ROOT_DIR=<python installation>.

By default, the resulting dynamic library will be installed in <MetaCG installation prefix>/lib/<python version>/site-packages. To override the installation path, use -DCMAKE_INSTALL_PYTHON_LIBDIR. Finally, make sure that the installation directory is found by Python, e.g., by using the PYTHONPATH environment variable.

Test whether pymetacg was built successfully:

$> python -c "import pymetacg; print(pymetacg.info)"

Usage

import pymetacg

# print MetaCG version information
print(pymetacg.info)

# read MetaCG file (.mcg)
cg = pymetacg.Callgraph.from_file("/path/to/callgraph.mcg")

# check whether node exists in graph
main_exists = "main" in cg

# get number of nodes in graph
num_nodes = len(cg)

# access nodes in graph (assumes function name to be unique in graph)
main = cg.get_single_node("main")
foo = cg.get_single_node("foo")

# shorthand for `get_single_node`
main = cg["main"]
foo = cg["foo"]

# access node by function name (ignoring possible other entries with same function name)
foo = cg.get_first_node("foo")

# get iterator over all nodes of name
for node in cg.get_nodes("foo")
    print(hash(node)) # get node ID
list_of_nodes = list(cg.get_nodes("foo"))

# iterate over node callees and collect callee function names into list
callees_names = [callee.function_name for callee in main.callees]

# iterate over node callers and collect caller function names into list
callers_names = [caller.function_name for caller in foo.callers]

# check whether function calls itself
foo_is_recursive = foo in foo.callees

# iterate over nodes in graph and print function names
for node in cg:
    print(node.function_name)

# check if node has some meta data
main_has_statement_metadata = "numStatements" in main.meta_data

# access JSON representation of node's meta data using `.data` (independent of meta data type)
metadata = main.meta_data["numStatements"].data

# global meta data can be accessed in the same way
global_metadata = cg.meta_data["someGlobalMetaData"]

Tests

pymetacg comes with a PyTest-based test suite, which can be enabled via the CMake option -DMETACG_BUILD_PYMETACG_TESTS=ON. The test suite requires pytest and pytest-cmake to be installed from PyPI.

# create and activate Python virtual environment
$> python -m venv .venv
$> source .venv/bin/activate

# install test dependencies from PyPI
$> pip install -r pymetacg/tests/requirements.txt

# configure and build MetaCG with pymetacg and its test suite enabled
$> cmake -S . -B build -G Ninja -DMETACG_BUILD_PYMETACG=ON -DMETACG_BUILD_PYMETACG_TESTS=ON
$> cmake --build build --parallel

# execute the tests
$> cd build/pymetacg/tets
$> ctest . --verbose