add PK_ENABLE_PROFILER

This commit is contained in:
blueloveTH 2024-04-27 20:01:58 +08:00
parent 4665fb3405
commit 6cc4d9c5cc
11 changed files with 85 additions and 49 deletions

View File

@ -29,6 +29,11 @@ if(PK_ENABLE_OS)
add_definitions(-DPK_ENABLE_OS=1)
endif()
option(PK_ENABLE_PROFILER "" OFF)
if(PK_ENABLE_PROFILER)
add_definitions(-DPK_ENABLE_PROFILER=1)
endif()
option(PK_NO_EXPORT_C_API "" OFF)
if(PK_NO_EXPORT_C_API)
add_definitions(-DPK_NO_EXPORT_C_API)

View File

@ -4,7 +4,7 @@ title: Debugging
---
!!!
This feature is available in `v1.4.5` or higher.
This feature is available in `v1.4.5` or higher. Set `PK_ENABLE_PROFILER` to `1` to enable this feature.
!!!
You can invoke `breakpoint()` in your python code to start a PDB-like session.

View File

@ -3,7 +3,9 @@ icon: package
label: line_profiler
---
Line-by-line profiler for Python.
!!!
This module is optional. Set `PK_ENABLE_PROFILER` to `1` to enable it.
!!!
## Example

View File

@ -13,6 +13,11 @@
#define PK_ENABLE_THREAD 0
#endif
// Enable `line_profiler` module and `breakpoint()` function
#ifndef PK_ENABLE_PROFILER // can be overridden by cmake
#define PK_ENABLE_PROFILER 0
#endif
// GC min threshold
#ifndef PK_GC_MIN_THRESHOLD // can be overridden by cmake
#define PK_GC_MIN_THRESHOLD 32768

View File

@ -139,8 +139,10 @@ public:
void (*_ceval_on_step)(VM*, Frame*, Bytecode bc) = nullptr;
#if PK_ENABLE_PROFILER
LineProfiler* _profiler = nullptr;
NextBreakpoint _next_breakpoint;
#endif
PrintFunc _stdout;
PrintFunc _stderr;
@ -173,10 +175,12 @@ public:
void _pop_frame(){
s_data.reset(callstack.top()._sp_base);
callstack.pop();
#if PK_ENABLE_PROFILER
if(!_next_breakpoint.empty() && callstack.size()<_next_breakpoint.callstack_size){
_next_breakpoint = NextBreakpoint();
}
#endif
}
PyObject* py_str(PyObject* obj);

View File

@ -1,6 +1,6 @@
python prebuild.py
SRC=$(find src/ -name "*.cpp")
clang++ -std=c++17 --coverage -O1 -stdlib=libc++ -frtti -Wfatal-errors -o main src2/main.cpp $SRC -Iinclude -DPK_ENABLE_OS=1 -DPK_DEBUG_PRECOMPILED_EXEC=1
clang++ -std=c++17 --coverage -O1 -stdlib=libc++ -frtti -Wfatal-errors -o main src2/main.cpp $SRC -Iinclude -DPK_ENABLE_OS=1 -DPK_DEBUG_PRECOMPILED_EXEC=1 -DPK_ENABLE_PROFILER=1
python scripts/run_tests.py

View File

@ -72,12 +72,17 @@ PyObject* VM::_run_top_frame(){
*/
{
#if PK_ENABLE_PROFILER
#define CEVAL_STEP_CALLBACK() \
if(_ceval_on_step) _ceval_on_step(this, frame, byte); \
if(_profiler) _profiler->_step(callstack.size(), frame); \
if(!_next_breakpoint.empty()) { _next_breakpoint._step(this); }
#else
#define CEVAL_STEP_CALLBACK() \
if(_ceval_on_step) _ceval_on_step(this, frame, byte);
#endif
#define DISPATCH_OP_CALL() { frame = top_frame(); goto __NEXT_FRAME; }
__NEXT_FRAME:
// cache
const CodeObject* co = frame->co;
@ -639,7 +644,10 @@ __NEXT_STEP:;
(byte.arg>>8) & 0xFF, // KWARGC
true
);
if(_0 == PY_OP_CALL) DISPATCH_OP_CALL();
if(_0 == PY_OP_CALL){
frame = top_frame();
goto __NEXT_FRAME;
}
PUSH(_0);
} DISPATCH();
TARGET(CALL_TP){
@ -671,7 +679,10 @@ __NEXT_STEP:;
true
);
}
if(_0 == PY_OP_CALL) DISPATCH_OP_CALL();
if(_0 == PY_OP_CALL){
frame = top_frame();
goto __NEXT_FRAME;
}
PUSH(_0);
} DISPATCH();
TARGET(RETURN_VALUE){
@ -931,13 +942,7 @@ __NEXT_STEP:;
} DISPATCH();
/*****************************************/
}
}
#undef DISPATCH
#undef TARGET
#undef DISPATCH_OP_CALL
#undef CEVAL_STEP_CALLBACK
/**********************************************************************/
PK_UNREACHABLE()
}catch(HandledException){
@ -970,6 +975,6 @@ __NEXT_STEP:;
#undef DISPATCH
#undef TARGET
#undef DISPATCH_OP_CALL
#undef CEVAL_STEP_CALLBACK
} // namespace pkpy

View File

@ -250,6 +250,43 @@ void add_module_gc(VM* vm){
vm->bind_func<0>(mod, "collect", PK_LAMBDA(VAR(vm->heap.collect())));
}
void add_module_enum(VM* vm){
PyObject* mod = vm->new_module("enum");
CodeObject_ code = vm->compile(kPythonLibs__enum, "enum.py", EXEC_MODE);
vm->_exec(code, mod);
PyObject* Enum = mod->attr("Enum");
vm->_all_types[PK_OBJ_GET(Type, Enum).index].on_end_subclass = \
[](VM* vm, PyTypeInfo* new_ti){
new_ti->subclass_enabled = false; // Enum class cannot be subclassed twice
NameDict& attr = new_ti->obj->attr();
for(auto [k, v]: attr.items()){
// wrap every attribute
std::string_view k_sv = k.sv();
if(k_sv.empty() || k_sv[0] == '_') continue;
attr.set(k, vm->call(new_ti->obj, VAR(k_sv), v));
}
};
}
void add_module___builtins(VM* vm){
PyObject* mod = vm->new_module("__builtins");
vm->bind_func<1>(mod, "next", [](VM* vm, ArgsView args){
return vm->py_next(args[0]);
});
vm->bind_func<1>(mod, "_enable_instance_dict", [](VM* vm, ArgsView args){
PyObject* self = args[0];
if(is_tagged(self)) vm->TypeError("object: tagged object cannot enable instance dict");
if(self->is_attr_valid()) vm->RuntimeError("object: instance dict is already enabled");
self->_enable_instance_dict();
return vm->None;
});
}
/************************************************/
#if PK_ENABLE_PROFILER
struct LineProfilerW;
struct _LpGuard{
PK_ALWAYS_PASS_BY_POINTER(_LpGuard)
@ -317,40 +354,10 @@ void add_module_line_profiler(VM *vm){
PyObject* mod = vm->new_module("line_profiler");
LineProfilerW::register_class(vm, mod);
}
void add_module_enum(VM* vm){
PyObject* mod = vm->new_module("enum");
CodeObject_ code = vm->compile(kPythonLibs__enum, "enum.py", EXEC_MODE);
vm->_exec(code, mod);
PyObject* Enum = mod->attr("Enum");
vm->_all_types[PK_OBJ_GET(Type, Enum).index].on_end_subclass = \
[](VM* vm, PyTypeInfo* new_ti){
new_ti->subclass_enabled = false; // Enum class cannot be subclassed twice
NameDict& attr = new_ti->obj->attr();
for(auto [k, v]: attr.items()){
// wrap every attribute
std::string_view k_sv = k.sv();
if(k_sv.empty() || k_sv[0] == '_') continue;
attr.set(k, vm->call(new_ti->obj, VAR(k_sv), v));
}
};
}
void add_module___builtins(VM* vm){
PyObject* mod = vm->new_module("__builtins");
vm->bind_func<1>(mod, "next", [](VM* vm, ArgsView args){
return vm->py_next(args[0]);
});
vm->bind_func<1>(mod, "_enable_instance_dict", [](VM* vm, ArgsView args){
PyObject* self = args[0];
if(is_tagged(self)) vm->TypeError("object: tagged object cannot enable instance dict");
if(self->is_attr_valid()) vm->RuntimeError("object: instance dict is already enabled");
self->_enable_instance_dict();
return vm->None;
});
#else
void add_module_line_profiler(VM* vm){
(void)vm;
}
#endif
} // namespace pkpy

View File

@ -73,7 +73,9 @@ void init_builtins(VM* _vm) {
// builtin functions
_vm->bind_func<0>(_vm->builtins, "breakpoint", [](VM* vm, ArgsView args) {
#if PK_ENABLE_PROFILER
vm->_next_breakpoint = NextBreakpoint(vm->callstack.size(), vm->top_frame()->curr_lineno(), false);
#endif
return vm->None;
});

View File

@ -1437,6 +1437,7 @@ void NextBreakpoint::_step(VM* vm){
}
void VM::_breakpoint(){
#if PK_ENABLE_PROFILER
_next_breakpoint = NextBreakpoint();
bool show_where = false;
@ -1569,6 +1570,7 @@ void VM::_breakpoint(){
continue;
}
}
#endif
}
} // namespace pkpy

View File

@ -1,4 +1,8 @@
from line_profiler import LineProfiler
try:
from line_profiler import LineProfiler
print('[INFO] line_profiler is used')
except ImportError:
exit(0)
def f2(x):
a = 0