From c4ab63ca15251efe72e1de5d70b1b862ec92934b Mon Sep 17 00:00:00 2001 From: blueloveTH Date: Wed, 28 May 2025 01:49:33 +0800 Subject: [PATCH] draft `LineProfiler` --- include/pocketpy/common/smallmap.h | 2 +- include/pocketpy/interpreter/line_profiler.h | 25 +++++++++ include/pocketpy/interpreter/vm.h | 2 + src/common/smallmap.c | 2 +- src/interpreter/ceval.c | 5 +- src/interpreter/line_profier.c | 56 ++++++++++++++++++++ src/interpreter/vm.c | 4 ++ 7 files changed, 93 insertions(+), 3 deletions(-) create mode 100644 include/pocketpy/interpreter/line_profiler.h create mode 100644 src/interpreter/line_profier.c diff --git a/include/pocketpy/common/smallmap.h b/include/pocketpy/common/smallmap.h index 4b8ed8d1..ad95857b 100644 --- a/include/pocketpy/common/smallmap.h +++ b/include/pocketpy/common/smallmap.h @@ -24,7 +24,7 @@ #define SMALLMAP_T__HEADER #define K void* -#define V int +#define V py_i64 #define NAME c11_smallmap_p2i #include "pocketpy/xmacros/smallmap.h" #undef SMALLMAP_T__HEADER diff --git a/include/pocketpy/interpreter/line_profiler.h b/include/pocketpy/interpreter/line_profiler.h new file mode 100644 index 00000000..4b5b345d --- /dev/null +++ b/include/pocketpy/interpreter/line_profiler.h @@ -0,0 +1,25 @@ +#pragma once + +#include "pocketpy/pocketpy.h" +#include + +#include "pocketpy/interpreter/frame.h" + +typedef struct LineRecord { + py_i64 hits; + clock_t time; +} LineRecord; + +typedef struct LineProfiler { + c11_smallmap_p2i records; // SourceData* -> LineRecord[] + SourceLocation prev_loc; + clock_t prev_time; +} LineProfiler; + +void LineProfiler__ctor(LineProfiler* self); +void LineProfiler__dtor(LineProfiler* self); +LineRecord* LineProfiler__get_record(LineProfiler* self, SourceLocation loc); +void LineProfiler__begin(LineProfiler* self, bool reset); +void LineProfiler__tracefunc_line(LineProfiler* self, py_Frame* frame); +void LineProfiler__end(LineProfiler* self); +void VM__set_line_profiler(VM* self, LineProfiler* profiler); diff --git a/include/pocketpy/interpreter/vm.h b/include/pocketpy/interpreter/vm.h index fbfe8964..2605e17b 100644 --- a/include/pocketpy/interpreter/vm.h +++ b/include/pocketpy/interpreter/vm.h @@ -7,6 +7,7 @@ #include "pocketpy/interpreter/frame.h" #include "pocketpy/interpreter/typeinfo.h" #include "pocketpy/interpreter/name.h" +#include "pocketpy/interpreter/line_profiler.h" #include // TODO: @@ -52,6 +53,7 @@ typedef struct VM { py_StackRef curr_decl_based_function; TraceInfo trace_info; WatchdogInfo watchdog_info; + LineProfiler* line_profiler; py_TValue vectorcall_buffer[PK_MAX_CO_VARNAMES]; InternedNames names; diff --git a/src/common/smallmap.c b/src/common/smallmap.c index ff6a8b34..3a265158 100644 --- a/src/common/smallmap.c +++ b/src/common/smallmap.c @@ -20,7 +20,7 @@ #define SMALLMAP_T__SOURCE #define K void* -#define V int +#define V py_i64 #define NAME c11_smallmap_p2i #include "pocketpy/xmacros/smallmap.h" #undef SMALLMAP_T__SOURCE diff --git a/src/interpreter/ceval.c b/src/interpreter/ceval.c index cbd789f3..66ebdb79 100644 --- a/src/interpreter/ceval.c +++ b/src/interpreter/ceval.c @@ -105,11 +105,14 @@ FrameResult VM__run_top_frame(VM* self) { PK_INCREF(loc.src); self->trace_info.prev_loc = loc; self->trace_info.func(frame, TRACE_EVENT_LINE); + if(self->line_profiler) { + LineProfiler__tracefunc_line(self->line_profiler, frame); + } } } #if PK_ENABLE_WATCHDOG - if(self->watchdog_info.max_reset_time > 0){ + if(self->watchdog_info.max_reset_time > 0) { clock_t now = clock(); if(now > self->watchdog_info.max_reset_time) { self->watchdog_info.max_reset_time = 0; diff --git a/src/interpreter/line_profier.c b/src/interpreter/line_profier.c new file mode 100644 index 00000000..ac5a8916 --- /dev/null +++ b/src/interpreter/line_profier.c @@ -0,0 +1,56 @@ +#include "pocketpy/interpreter/line_profiler.h" + +void LineProfiler__ctor(LineProfiler* self) { + c11_smallmap_p2i__ctor(&self->records); + self->prev_loc.src = NULL; + self->prev_time = 0; +} + +void LineProfiler__dtor(LineProfiler* self) { + for(int i = 0; i < self->records.length; i++) { + LineRecord* lines = c11__getitem(LineRecord*, &self->records, i); + PK_FREE(lines); + } + c11_smallmap_p2i__dtor(&self->records); +} + +LineRecord* LineProfiler__get_record(LineProfiler* self, SourceLocation loc) { + LineRecord* lines = (LineRecord*)c11_smallmap_p2i__get(&self->records, loc.src, 0); + if(lines == NULL) { + int max_lineno = loc.src->line_starts.length; + lines = PK_MALLOC(sizeof(LineRecord) * (max_lineno + 1)); + memset(lines, 0, sizeof(LineRecord) * (max_lineno + 1)); + c11_smallmap_p2i__set(&self->records, loc.src, (py_i64)lines); + } + return &lines[loc.lineno]; +} + +void LineProfiler__begin(LineProfiler* self, bool reset) { + if(self->records.length > 0 && reset) { + LineProfiler__dtor(self); + LineProfiler__ctor(self); + } + self->prev_loc.src = NULL; + self->prev_time = 0; + VM__set_line_profiler(pk_current_vm, self); +} + +void LineProfiler__tracefunc_line(LineProfiler* self, py_Frame* frame) { + clock_t now = clock(); + if(self->prev_loc.src != NULL) { + LineRecord* line = LineProfiler__get_record(self, self->prev_loc); + line->hits++; + line->time += now - self->prev_time; + } + self->prev_loc = Frame__source_location(frame); + self->prev_time = now; +} + +void LineProfiler__end(LineProfiler* self) { + if(self->prev_loc.src != NULL) { + LineRecord* line = LineProfiler__get_record(self, self->prev_loc); + line->hits++; + line->time += clock() - self->prev_time; + } + VM__set_line_profiler(pk_current_vm, NULL); +} diff --git a/src/interpreter/vm.c b/src/interpreter/vm.c index 4d49e2df..43a6fe59 100644 --- a/src/interpreter/vm.c +++ b/src/interpreter/vm.c @@ -85,6 +85,8 @@ void VM__ctor(VM* self) { self->curr_class = NULL; self->curr_decl_based_function = NULL; memset(&self->trace_info, 0, sizeof(TraceInfo)); + memset(&self->watchdog_info, 0, sizeof(WatchdogInfo)); + self->line_profiler = NULL; FixedMemoryPool__ctor(&self->pool_frame, sizeof(py_Frame), 32); @@ -269,6 +271,8 @@ void VM__dtor(VM* self) { InternedNames__dtor(&self->names); } +void VM__set_line_profiler(VM* self, LineProfiler* profiler) { self->line_profiler = profiler; } + void VM__push_frame(VM* self, py_Frame* frame) { frame->f_back = self->top_frame; self->top_frame = frame;