mirror of
https://github.com/JakeHillion/drgn.git
synced 2024-12-23 09:43:06 +00:00
b899a10836
enum drgn_register_number in the public libdrgn API and drgn.Register.number in the Python bindings are basically exports of DWARF register numbers. They only exist as a way to identify registers that's lighter weight than string lookups. libdrgn already has struct drgn_register, so we can use that to identify registers in the public API and remove enum drgn_register_number. This has a couple of benefits: we don't depend on DWARF numbering in our API, and we don't have to generate drgn.h from the architecture files. The Python bindings can just use string names for now. If it seems useful, StackFrame.register() can take a Register in the future, we'll just need to be careful to not allow Registers from the wrong platform. While we're changing the API anyways, also change it so that registers have a list of names instead of one name. This isn't needed for x86-64 at the moment, but will be for architectures that have multiple names for the same register (like ARM). Signed-off-by: Omar Sandoval <osandov@osandov.com>
301 lines
8.7 KiB
C
301 lines
8.7 KiB
C
// Copyright (c) Facebook, Inc. and its affiliates.
|
|
// SPDX-License-Identifier: GPL-3.0+
|
|
|
|
#ifdef WITH_KDUMPFILE
|
|
#include <libkdumpfile/kdumpfile.h>
|
|
#endif
|
|
|
|
#include "drgnpy.h"
|
|
#include "../path.h"
|
|
|
|
/* Basically PyModule_AddType(), which is only available since Python 3.9. */
|
|
static int add_type(PyObject *module, PyTypeObject *type)
|
|
{
|
|
int ret = PyType_Ready(type);
|
|
if (ret)
|
|
return ret;
|
|
const char *name = type->tp_name;
|
|
const char *dot = strrchr(type->tp_name, '.');
|
|
if (dot)
|
|
name = dot + 1;
|
|
Py_INCREF(type);
|
|
ret = PyModule_AddObject(module, name, (PyObject *)type);
|
|
if (ret)
|
|
Py_DECREF(type);
|
|
return ret;
|
|
}
|
|
|
|
PyObject *MissingDebugInfoError;
|
|
PyObject *ObjectAbsentError;
|
|
PyObject *OutOfBoundsError;
|
|
|
|
static PyObject *filename_matches(PyObject *self, PyObject *args,
|
|
PyObject *kwds)
|
|
{
|
|
static char *keywords[] = {"haystack", "needle", NULL};
|
|
struct path_arg haystack_arg = {.allow_none = true};
|
|
struct path_arg needle_arg = {.allow_none = true};
|
|
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O&O&:filename_matches",
|
|
keywords, path_converter,
|
|
&haystack_arg, path_converter,
|
|
&needle_arg))
|
|
return NULL;
|
|
|
|
struct path_iterator haystack = {
|
|
.components = (struct path_iterator_component [1]){},
|
|
.num_components = 0,
|
|
};
|
|
if (haystack_arg.path) {
|
|
haystack.components[0].path = haystack_arg.path;
|
|
haystack.components[0].len = haystack_arg.length;
|
|
haystack.num_components = 1;
|
|
}
|
|
struct path_iterator needle = {
|
|
.components = (struct path_iterator_component [1]){},
|
|
.num_components = 0,
|
|
};
|
|
if (needle_arg.path) {
|
|
needle.components[0].path = needle_arg.path;
|
|
needle.components[0].len = needle_arg.length;
|
|
needle.num_components = 1;
|
|
}
|
|
bool ret = path_ends_with(&haystack, &needle);
|
|
path_cleanup(&haystack_arg);
|
|
path_cleanup(&needle_arg);
|
|
Py_RETURN_BOOL(ret);
|
|
}
|
|
|
|
static PyObject *sizeof_(PyObject *self, PyObject *arg)
|
|
{
|
|
struct drgn_error *err;
|
|
uint64_t size;
|
|
|
|
if (PyObject_TypeCheck(arg, &DrgnType_type)) {
|
|
err = drgn_type_sizeof(((DrgnType *)arg)->type, &size);
|
|
} else if (PyObject_TypeCheck(arg, &DrgnObject_type)) {
|
|
err = drgn_object_sizeof(&((DrgnObject *)arg)->obj, &size);
|
|
} else {
|
|
return PyErr_Format(PyExc_TypeError,
|
|
"expected Type or Object, not %s",
|
|
Py_TYPE(arg)->tp_name);
|
|
}
|
|
if (err)
|
|
return set_drgn_error(err);
|
|
return PyLong_FromUnsignedLongLong(size);
|
|
}
|
|
|
|
static PyObject *offsetof_(PyObject *self, PyObject *args, PyObject *kwds)
|
|
{
|
|
struct drgn_error *err;
|
|
|
|
static char *keywords[] = {"type", "member", NULL};
|
|
DrgnType *type;
|
|
const char *member;
|
|
if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!s:offsetof", keywords,
|
|
&DrgnType_type, &type, &member))
|
|
return NULL;
|
|
uint64_t offset;
|
|
err = drgn_type_offsetof(type->type, member, &offset);
|
|
if (err)
|
|
return set_drgn_error(err);
|
|
return PyLong_FromUnsignedLongLong(offset);
|
|
}
|
|
|
|
static PyMethodDef drgn_methods[] = {
|
|
{"filename_matches", (PyCFunction)filename_matches,
|
|
METH_VARARGS | METH_KEYWORDS, drgn_filename_matches_DOC},
|
|
{"NULL", (PyCFunction)DrgnObject_NULL, METH_VARARGS | METH_KEYWORDS,
|
|
drgn_NULL_DOC},
|
|
{"sizeof", (PyCFunction)sizeof_, METH_O, drgn_sizeof_DOC},
|
|
{"offsetof", (PyCFunction)offsetof_, METH_VARARGS | METH_KEYWORDS,
|
|
drgn_offsetof_DOC},
|
|
{"cast", (PyCFunction)cast, METH_VARARGS | METH_KEYWORDS,
|
|
drgn_cast_DOC},
|
|
{"reinterpret", (PyCFunction)reinterpret, METH_VARARGS | METH_KEYWORDS,
|
|
drgn_reinterpret_DOC},
|
|
{"container_of", (PyCFunction)DrgnObject_container_of,
|
|
METH_VARARGS | METH_KEYWORDS, drgn_container_of_DOC},
|
|
{"program_from_core_dump", (PyCFunction)program_from_core_dump,
|
|
METH_VARARGS | METH_KEYWORDS, drgn_program_from_core_dump_DOC},
|
|
{"program_from_kernel", (PyCFunction)program_from_kernel,
|
|
METH_NOARGS, drgn_program_from_kernel_DOC},
|
|
{"program_from_pid", (PyCFunction)program_from_pid,
|
|
METH_VARARGS | METH_KEYWORDS, drgn_program_from_pid_DOC},
|
|
{"_linux_helper_read_vm", (PyCFunction)drgnpy_linux_helper_read_vm,
|
|
METH_VARARGS | METH_KEYWORDS},
|
|
{"_linux_helper_radix_tree_lookup",
|
|
(PyCFunction)drgnpy_linux_helper_radix_tree_lookup,
|
|
METH_VARARGS | METH_KEYWORDS},
|
|
{"_linux_helper_idr_find", (PyCFunction)drgnpy_linux_helper_idr_find,
|
|
METH_VARARGS | METH_KEYWORDS},
|
|
{"_linux_helper_find_pid", (PyCFunction)drgnpy_linux_helper_find_pid,
|
|
METH_VARARGS | METH_KEYWORDS},
|
|
{"_linux_helper_pid_task", (PyCFunction)drgnpy_linux_helper_pid_task,
|
|
METH_VARARGS | METH_KEYWORDS},
|
|
{"_linux_helper_find_task", (PyCFunction)drgnpy_linux_helper_find_task,
|
|
METH_VARARGS | METH_KEYWORDS},
|
|
{"_linux_helper_kaslr_offset",
|
|
(PyCFunction)drgnpy_linux_helper_kaslr_offset,
|
|
METH_VARARGS | METH_KEYWORDS},
|
|
{"_linux_helper_pgtable_l5_enabled",
|
|
(PyCFunction)drgnpy_linux_helper_pgtable_l5_enabled,
|
|
METH_VARARGS | METH_KEYWORDS},
|
|
{},
|
|
};
|
|
|
|
static struct PyModuleDef drgnmodule = {
|
|
PyModuleDef_HEAD_INIT,
|
|
"_drgn",
|
|
drgn_DOC,
|
|
-1,
|
|
drgn_methods,
|
|
};
|
|
|
|
/*
|
|
* These are for type checking and aren't strictly required at runtime, but
|
|
* adding them anyways results in better pydoc output and saves us from fiddling
|
|
* with typing.TYPE_CHECKING/forward references.
|
|
*/
|
|
static int add_type_aliases(PyObject *m)
|
|
{
|
|
/*
|
|
* This should be a subclass of typing.Protocol, but that is only
|
|
* available since Python 3.8.
|
|
*/
|
|
PyObject *IntegerLike = PyType_FromSpec(&(PyType_Spec){
|
|
.name = "_drgn.IntegerLike",
|
|
.flags = Py_TPFLAGS_DEFAULT,
|
|
.slots = (PyType_Slot []){{0, NULL}},
|
|
});
|
|
if (!IntegerLike)
|
|
return -1;
|
|
if (PyModule_AddObject(m, "IntegerLike", IntegerLike) == -1) {
|
|
Py_DECREF(IntegerLike);
|
|
return -1;
|
|
}
|
|
|
|
PyObject *os_module = PyImport_ImportModule("os");
|
|
if (!os_module)
|
|
return -1;
|
|
PyObject *os_PathLike = PyObject_GetAttrString(os_module, "PathLike");
|
|
Py_DECREF(os_module);
|
|
if (!os_PathLike)
|
|
return -1;
|
|
PyObject *item = Py_BuildValue("OOO", &PyUnicode_Type, &PyBytes_Type,
|
|
os_PathLike);
|
|
Py_DECREF(os_PathLike);
|
|
if (!item)
|
|
return -1;
|
|
|
|
PyObject *typing_module = PyImport_ImportModule("typing");
|
|
if (!typing_module) {
|
|
Py_DECREF(item);
|
|
return -1;
|
|
}
|
|
PyObject *typing_Union = PyObject_GetAttrString(typing_module, "Union");
|
|
Py_DECREF(typing_module);
|
|
if (!typing_Union) {
|
|
Py_DECREF(item);
|
|
return -1;
|
|
}
|
|
|
|
PyObject *Path = PyObject_GetItem(typing_Union, item);
|
|
Py_DECREF(typing_Union);
|
|
Py_DECREF(item);
|
|
if (!Path)
|
|
return -1;
|
|
if (PyModule_AddObject(m, "Path", Path) == -1) {
|
|
Py_DECREF(Path);
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
DRGNPY_PUBLIC PyMODINIT_FUNC PyInit__drgn(void)
|
|
{
|
|
PyObject *m = PyModule_Create(&drgnmodule);
|
|
if (!m)
|
|
return NULL;
|
|
|
|
if (add_module_constants(m) ||
|
|
add_type_aliases(m) ||
|
|
add_type(m, &Language_type) || add_languages() ||
|
|
add_type(m, &DrgnObject_type) ||
|
|
PyType_Ready(&ObjectIterator_type) ||
|
|
add_type(m, &Platform_type) ||
|
|
add_type(m, &Program_type) ||
|
|
add_type(m, &Register_type) ||
|
|
add_type(m, &StackFrame_type) ||
|
|
add_type(m, &StackTrace_type) ||
|
|
add_type(m, &Symbol_type) ||
|
|
add_type(m, &DrgnType_type) ||
|
|
add_type(m, &TypeEnumerator_type) ||
|
|
add_type(m, &TypeMember_type) ||
|
|
add_type(m, &TypeParameter_type) ||
|
|
add_type(m, &TypeTemplateParameter_type))
|
|
goto err;
|
|
|
|
FaultError_type.tp_base = (PyTypeObject *)PyExc_Exception;
|
|
if (add_type(m, &FaultError_type))
|
|
goto err;
|
|
|
|
MissingDebugInfoError =
|
|
PyErr_NewExceptionWithDoc("_drgn.MissingDebugInfoError",
|
|
drgn_MissingDebugInfoError_DOC, NULL,
|
|
NULL);
|
|
if (!MissingDebugInfoError)
|
|
goto err;
|
|
if (PyModule_AddObject(m, "MissingDebugInfoError",
|
|
MissingDebugInfoError)) {
|
|
Py_DECREF(MissingDebugInfoError);
|
|
goto err;
|
|
}
|
|
|
|
ObjectAbsentError =
|
|
PyErr_NewExceptionWithDoc("_drgn.ObjectAbsentError",
|
|
drgn_ObjectAbsentError_DOC,
|
|
NULL, NULL);
|
|
if (!ObjectAbsentError)
|
|
goto err;
|
|
if (PyModule_AddObject(m, "ObjectAbsentError", ObjectAbsentError)) {
|
|
Py_DECREF(ObjectAbsentError);
|
|
goto err;
|
|
}
|
|
|
|
OutOfBoundsError = PyErr_NewExceptionWithDoc("_drgn.OutOfBoundsError",
|
|
drgn_OutOfBoundsError_DOC,
|
|
NULL, NULL);
|
|
if (!OutOfBoundsError)
|
|
goto err;
|
|
if (PyModule_AddObject(m, "OutOfBoundsError", OutOfBoundsError)) {
|
|
Py_DECREF(OutOfBoundsError);
|
|
goto err;
|
|
}
|
|
|
|
PyObject *host_platform_obj = Platform_wrap(&drgn_host_platform);
|
|
if (!host_platform_obj)
|
|
goto err;
|
|
if (PyModule_AddObject(m, "host_platform", host_platform_obj)) {
|
|
Py_DECREF(host_platform_obj);
|
|
goto err;
|
|
}
|
|
|
|
PyObject *with_libkdumpfile;
|
|
#ifdef WITH_LIBKDUMPFILE
|
|
with_libkdumpfile = Py_True;
|
|
#else
|
|
with_libkdumpfile = Py_False;
|
|
#endif
|
|
Py_INCREF(with_libkdumpfile);
|
|
if (PyModule_AddObject(m, "_with_libkdumpfile", with_libkdumpfile)) {
|
|
Py_DECREF(with_libkdumpfile);
|
|
goto err;
|
|
}
|
|
|
|
return m;
|
|
|
|
err:
|
|
Py_DECREF(m);
|
|
return NULL;
|
|
}
|