mirror of
https://github.com/pocketpy/pocketpy
synced 2025-10-20 11:30:18 +00:00
add PK_ENABLE_PROFILER
This commit is contained in:
parent
4665fb3405
commit
6cc4d9c5cc
@ -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)
|
||||||
|
@ -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.
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
@ -174,9 +176,11 @@ public:
|
|||||||
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);
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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
|
@ -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;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -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
|
@ -1,4 +1,8 @@
|
|||||||
|
try:
|
||||||
from line_profiler import LineProfiler
|
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
|
||||||
|
Loading…
x
Reference in New Issue
Block a user