diff --git a/.gitignore b/.gitignore index 5d28157c..56dfad56 100644 --- a/.gitignore +++ b/.gitignore @@ -40,4 +40,5 @@ docs/C-API/functions.md cmake-build-* -tmp/ \ No newline at end of file +tmp/ +profiler_report.json diff --git a/include/pocketpy/interpreter/line_profiler.h b/include/pocketpy/interpreter/line_profiler.h index 9e108ee6..9eaa5c98 100644 --- a/include/pocketpy/interpreter/line_profiler.h +++ b/include/pocketpy/interpreter/line_profiler.h @@ -24,4 +24,6 @@ void LineProfiler__begin(LineProfiler* self); void LineProfiler__tracefunc_line(LineProfiler* self, py_Frame* frame); void LineProfiler__end(LineProfiler* self); void LineProfiler__reset(LineProfiler* self); -c11_string* LineProfiler__get_report(LineProfiler* self); \ No newline at end of file +c11_string* LineProfiler__get_report(LineProfiler* self); + +void LineProfiler_tracefunc(py_Frame* frame, enum py_TraceEvent event); \ No newline at end of file diff --git a/include/pocketpy/pocketpy.h b/include/pocketpy/pocketpy.h index 7c8ea276..ecd00b59 100644 --- a/include/pocketpy/pocketpy.h +++ b/include/pocketpy/pocketpy.h @@ -163,9 +163,6 @@ PK_API void py_Frame_newlocals(py_Frame* frame, py_OutRef out); /// Returns `NULL` if not available. PK_API py_StackRef py_Frame_function(py_Frame* frame); -/// Trace function for the line profiler. -PK_API void py_LineProfiler_tracefunc(py_Frame* frame, enum py_TraceEvent event); - /// Run a source string. /// @param source source string. /// @param filename filename (for error messages). @@ -704,6 +701,14 @@ PK_API bool py_json_loads(const char* source) PY_RAISE PY_RETURN; PK_API bool py_pickle_dumps(py_Ref val) PY_RAISE PY_RETURN; /// Python equivalent to `pickle.loads(val)`. PK_API bool py_pickle_loads(const unsigned char* data, int size) PY_RAISE PY_RETURN; + +/************* Profiler *************/ + +PK_API void py_profiler_begin(); +PK_API void py_profiler_end(); +PK_API void py_profiler_reset(); +PK_API char* py_profiler_report(); + /************* Unchecked Functions *************/ PK_API py_ObjectRef py_tuple_data(py_Ref self); diff --git a/src/interpreter/line_profier.c b/src/interpreter/line_profiler.c similarity index 92% rename from src/interpreter/line_profier.c rename to src/interpreter/line_profiler.c index 966aec79..864e5516 100644 --- a/src/interpreter/line_profier.c +++ b/src/interpreter/line_profiler.c @@ -66,6 +66,12 @@ void LineProfiler__reset(LineProfiler* self) { c11_string* LineProfiler__get_report(LineProfiler* self) { c11_sbuf sbuf; c11_sbuf__ctor(&sbuf); + c11_sbuf__write_char(&sbuf, '{'); + c11_sbuf__write_cstr(&sbuf, "\"version\": 1, "); + c11_sbuf__write_cstr(&sbuf, "\"CLOCKS_PER_SEC\": "); + c11_sbuf__write_i64(&sbuf, CLOCKS_PER_SEC); + c11_sbuf__write_cstr(&sbuf, ", \"records\": "); + c11_sbuf__write_char(&sbuf, '{'); for(int i = 0; i < self->records.length; i++) { c11_smallmap_p2i_KV kv = c11__getitem(c11_smallmap_p2i_KV, &self->records, i); @@ -90,5 +96,6 @@ c11_string* LineProfiler__get_report(LineProfiler* self) { if(i < self->records.length - 1) c11_sbuf__write_cstr(&sbuf, ", "); } c11_sbuf__write_char(&sbuf, '}'); + c11_sbuf__write_char(&sbuf, '}'); return c11_sbuf__submit(&sbuf); } diff --git a/src/interpreter/vm.c b/src/interpreter/vm.c index a20b4d77..f7b460d9 100644 --- a/src/interpreter/vm.c +++ b/src/interpreter/vm.c @@ -34,9 +34,36 @@ static void pk_default_flush() { fflush(stdout); } static int pk_default_getchr() { return getchar(); } -void py_LineProfiler_tracefunc(py_Frame* frame, enum py_TraceEvent event) { - LineProfiler* self = &pk_current_vm->line_profiler; - if(self->enabled && event == TRACE_EVENT_LINE) LineProfiler__tracefunc_line(self, frame); +void py_profiler_begin() { + LineProfiler* lp = &pk_current_vm->line_profiler; + TraceInfo* trace_info = &pk_current_vm->trace_info; + if(trace_info->func == NULL) py_sys_settrace(LineProfiler_tracefunc, true); + c11__rtassert(trace_info->func == LineProfiler_tracefunc); + LineProfiler__begin(lp); +} + +void py_profiler_end() { + LineProfiler* lp = &pk_current_vm->line_profiler; + LineProfiler__end(lp); +} + +void py_profiler_reset() { + LineProfiler* lp = &pk_current_vm->line_profiler; + LineProfiler__reset(lp); +} + +char* py_profiler_report() { + LineProfiler* lp = &pk_current_vm->line_profiler; + if(lp->enabled) LineProfiler__end(lp); + c11_string* s = LineProfiler__get_report(lp); + char* s_dup = c11_strdup(s->data); + c11_string__delete(s); + return s_dup; +} + +void LineProfiler_tracefunc(py_Frame* frame, enum py_TraceEvent event) { + LineProfiler* lp = &pk_current_vm->line_profiler; + if(lp->enabled && event == TRACE_EVENT_LINE) LineProfiler__tracefunc_line(lp, frame); } static int BinTree__cmp_cstr(void* lhs, void* rhs) { diff --git a/src/modules/pkpy.c b/src/modules/pkpy.c index 1bc2661d..d8c89b90 100644 --- a/src/modules/pkpy.c +++ b/src/modules/pkpy.c @@ -469,8 +469,10 @@ static void pkpy_configmacros_add(py_Ref dict, const char* key, int val) { static bool pkpy_profiler_begin(int argc, py_Ref argv) { PY_CHECK_ARGC(0); - if(pk_current_vm->trace_info.func != py_LineProfiler_tracefunc) { - return RuntimeError("py_LineProfiler_tracefunc() should be set as the trace function"); + TraceInfo* trace_info = &pk_current_vm->trace_info; + if(trace_info->func == NULL) py_sys_settrace(LineProfiler_tracefunc, true); + if(trace_info->func != LineProfiler_tracefunc) { + return RuntimeError("LineProfiler_tracefunc() should be set as the trace function"); } LineProfiler__begin(&pk_current_vm->line_profiler); py_newnone(py_retval()); diff --git a/src2/main.c b/src2/main.c index 9cebc1f9..3ee066ad 100644 --- a/src2/main.c +++ b/src2/main.c @@ -78,10 +78,23 @@ int main(int argc, char** argv) { } } } else { - if(profile) py_sys_settrace(py_LineProfiler_tracefunc, true); + if(profile) py_profiler_begin(); char* source = read_file(filename); if(source) { - if(!py_exec(source, filename, EXEC_MODE, NULL)) py_printexc(); + if(!py_exec(source, filename, EXEC_MODE, NULL)) + py_printexc(); + else { + if(profile) { + char* json_report = py_profiler_report(); + FILE* report_file = fopen("profiler_report.json", "w"); + if(report_file) { + fprintf(report_file, "%s", json_report); + fclose(report_file); + } + PK_FREE(json_report); + } + } + PK_FREE(source); } }