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) add_definitions(-DPK_ENABLE_OS=1)
endif() endif()
option(PK_ENABLE_PROFILER "" OFF)
if(PK_ENABLE_PROFILER)
add_definitions(-DPK_ENABLE_PROFILER=1)
endif()
option(PK_NO_EXPORT_C_API "" OFF) option(PK_NO_EXPORT_C_API "" OFF)
if(PK_NO_EXPORT_C_API) if(PK_NO_EXPORT_C_API)
add_definitions(-DPK_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. 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 label: line_profiler
--- ---
Line-by-line profiler for Python. !!!
This module is optional. Set `PK_ENABLE_PROFILER` to `1` to enable it.
!!!
## Example ## Example

View File

@ -13,6 +13,11 @@
#define PK_ENABLE_THREAD 0 #define PK_ENABLE_THREAD 0
#endif #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 // GC min threshold
#ifndef PK_GC_MIN_THRESHOLD // can be overridden by cmake #ifndef PK_GC_MIN_THRESHOLD // can be overridden by cmake
#define PK_GC_MIN_THRESHOLD 32768 #define PK_GC_MIN_THRESHOLD 32768

View File

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

View File

@ -1,6 +1,6 @@
python prebuild.py python prebuild.py
SRC=$(find src/ -name "*.cpp") 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 python scripts/run_tests.py

View File

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

View File

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

View File

@ -1437,6 +1437,7 @@ void NextBreakpoint::_step(VM* vm){
} }
void VM::_breakpoint(){ void VM::_breakpoint(){
#if PK_ENABLE_PROFILER
_next_breakpoint = NextBreakpoint(); _next_breakpoint = NextBreakpoint();
bool show_where = false; bool show_where = false;
@ -1569,6 +1570,7 @@ void VM::_breakpoint(){
continue; continue;
} }
} }
#endif
} }
} // namespace pkpy } // 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): def f2(x):
a = 0 a = 0