소스 검색

fix a bug of `line_profiler`

blueloveTH 2 년 전
부모
커밋
c3b050c77a
9개의 변경된 파일82개의 추가작업 그리고 39개의 파일을 삭제
  1. 4 3
      amalgamate.py
  2. 8 0
      include/pocketpy/frame.h
  3. 7 4
      include/pocketpy/profiler.h
  4. 0 8
      include/pocketpy/vm.h
  5. 2 1
      prebuild.py
  6. 2 1
      scripts/genstub.py
  7. 1 1
      src/ceval.cpp
  8. 45 17
      src/profiler.cpp
  9. 13 4
      tests/84_line_profiler.py

+ 4 - 3
amalgamate.py

@@ -60,7 +60,8 @@ for seq in pipeline:
 				text += remove_copied_include(f.read()) + '\n'
 				copied.add(j)
 
-with open("amalgamated/pocketpy.h", "wt", encoding='utf-8') as f:
+# use LF line endings instead of CRLF
+with open("amalgamated/pocketpy.h", "wt", encoding='utf-8', newline='\n') as f:
 	final_text = \
 r'''/*
  *  Copyright (c) 2023 blueloveTH
@@ -77,7 +78,7 @@ shutil.copy("src2/main.cpp", "amalgamated/main.cpp")
 with open("amalgamated/main.cpp", "rt", encoding='utf-8') as f:
 	text = f.read()
 text = text.replace('#include "pocketpy/pocketpy.h"', '#include "pocketpy.h"')
-with open("amalgamated/main.cpp", "wt", encoding='utf-8') as f:
+with open("amalgamated/main.cpp", "wt", encoding='utf-8', newline='\n') as f:
 	f.write(text)
 
 if sys.platform in ['linux', 'darwin']:
@@ -89,7 +90,7 @@ print("amalgamated/pocketpy.h")
 
 def sync(path):
 	shutil.copy("amalgamated/pocketpy.h", os.path.join(path, "pocketpy.h"))
-	with open(os.path.join(path, "pocketpy.cpp"), "wt", encoding='utf-8') as f:
+	with open(os.path.join(path, "pocketpy.cpp"), "wt", encoding='utf-8', newline='\n') as f:
 		f.write("#include \"pocketpy.h\"\n")
 
 sync("plugins/macos/pocketpy")

+ 8 - 0
include/pocketpy/frame.h

@@ -127,4 +127,12 @@ struct Frame {
     }
 };
 
+struct FrameId{
+    std::vector<pkpy::Frame>* data;
+    int index;
+    FrameId(std::vector<pkpy::Frame>* data, int index) : data(data), index(index) {}
+    Frame* operator->() const { return &data->operator[](index); }
+    Frame* get() const { return &data->operator[](index); }
+};
+
 }; // namespace pkpy

+ 7 - 4
include/pocketpy/profiler.h

@@ -13,19 +13,22 @@ struct LineRecord{
     bool is_valid() const { return line != -1; }
 };
 
-struct LineProfiler{
+struct FrameRecord{
+    FrameId frame;
     clock_t prev_time;
     LineRecord* prev_record;
     int prev_line;
+};
 
+struct LineProfiler{
     // filename -> records
     std::map<std::string_view, std::vector<LineRecord>> records;
-
+    stack<FrameRecord> frames;
     std::set<FuncDecl*> functions;
 
     void begin();
-    void _step(Frame* frame);
-    void _step_end();
+    void _step(FrameId frame);
+    void _step_end(FrameId frame);
     void end();
     Str stats();
 };

+ 0 - 8
include/pocketpy/vm.h

@@ -105,14 +105,6 @@ struct PyTypeInfo{
 
 };
 
-struct FrameId{
-    std::vector<pkpy::Frame>* data;
-    int index;
-    FrameId(std::vector<pkpy::Frame>* data, int index) : data(data), index(index) {}
-    Frame* operator->() const { return &data->operator[](index); }
-    Frame* get() const { return &data->operator[](index); }
-};
-
 typedef void(*PrintFunc)(const char*, int);
 
 class VM {

+ 2 - 1
prebuild.py

@@ -36,5 +36,6 @@ namespace pkpy{
 '''
     return header
 
-with open("include/pocketpy/_generated.h", "w", encoding='utf-8') as f:
+# use LF line endings instead of CRLF
+with open("include/pocketpy/_generated.h", "wt", encoding='utf-8', newline='\n') as f:
     f.write(generate_python_sources())

+ 2 - 1
scripts/genstub.py

@@ -25,7 +25,8 @@ for line in lines:
         ret + ' ' + body + ' {\n' + mock_string + '\n}\n'
     )
 
-with open('src2/pocketpy_c.c', 'w') as f:
+# use LF line endings instead of CRLF
+with open('src2/pocketpy_c.c', 'wt', encoding='utf-8', newline='\n') as f:
     f.write('''
 #include "pocketpy_c.h"
 

+ 1 - 1
src/ceval.cpp

@@ -68,7 +68,7 @@ PyObject* VM::_run_top_frame(){
 
 #define CEVAL_STEP_CALLBACK() \
     if(_ceval_on_step) _ceval_on_step(this, frame.get(), byte); \
-    if(_profiler) _profiler->_step(frame.get());
+    if(_profiler) _profiler->_step(frame);
 
 #define DISPATCH_OP_CALL() { frame = top_frame(); goto __NEXT_FRAME; }
 __NEXT_FRAME:

+ 45 - 17
src/profiler.cpp

@@ -15,47 +15,75 @@ static std::string to_string_1f(f64 x){
 }
 
 void LineProfiler::begin(){
-    prev_time = 0;
-    prev_record = nullptr;
-    prev_line = -1;
-    records.clear();
+    frames.clear();
 }
 
-void LineProfiler::_step(Frame *frame){
+void LineProfiler::_step(FrameId frame){
     std::string_view filename = frame->co->src->filename.sv();
     int line = frame->co->lines[frame->_ip];
-    // std::string_view function = frame->co->name.sv();
 
-    if(prev_record == nullptr){
-        prev_time = clock();
+    if(frames.empty()){
+        frames.push({frame, clock(), nullptr, -1});
     }else{
-        _step_end();
+        _step_end(frame);
     }
 
     std::vector<LineRecord>& file_records = records[filename];
     if(file_records.empty()){
+        // initialize file_records
         int total_lines = frame->co->src->line_starts.size();
         file_records.resize(total_lines + 1);
         for(int i=1; i<=total_lines; i++){
             file_records[i].line = i;
         }
     }
-    prev_record = &file_records.at(line);
+
+    frames.top().prev_record = &file_records.at(line);
 }
 
-void LineProfiler::_step_end(){
+void LineProfiler::_step_end(FrameId frame){
     clock_t now = clock();
-    clock_t delta = now - prev_time;
-    prev_time = now;
-    if(prev_record->line != prev_line){
+    FrameRecord& top_frame_record = frames.top();
+    LineRecord* prev_record = top_frame_record.prev_record;
+
+    if(prev_record->line != top_frame_record.prev_line){
         prev_record->hits++;
-        prev_line = prev_record->line;
+        top_frame_record.prev_line = prev_record->line;
+    }
+
+    int id_delta = frame.index - top_frame_record.frame.index;
+    PK_ASSERT(id_delta >= -1 && id_delta <= 1);
+    
+    if(id_delta == 1){
+        frames.push({frame, now, nullptr, -1});
+    }else{
+        clock_t delta = now - top_frame_record.prev_time;
+        top_frame_record.prev_time = now;
+        prev_record->time += delta;
+        if(id_delta == -1){
+            frames.pop();
+            prev_record = frames.top().prev_record;
+            prev_record->time += (now - frames.top().prev_time);
+        }
     }
-    prev_record->time += delta;
 }
 
 void LineProfiler::end(){
-    _step_end();
+    clock_t now = clock();
+    FrameRecord& top_frame_record = frames.top();
+    LineRecord* prev_record = top_frame_record.prev_record;
+
+    if(prev_record->line != top_frame_record.prev_line){
+        prev_record->hits++;
+        top_frame_record.prev_line = prev_record->line;
+    }
+
+    clock_t delta = now - top_frame_record.prev_time;
+    top_frame_record.prev_time = now;
+    prev_record->time += delta;
+
+    frames.pop();
+    PK_ASSERT(frames.empty());
 }
 
 Str LineProfiler::stats(){

+ 13 - 4
tests/84_line_profiler.py

@@ -1,15 +1,24 @@
 from line_profiler import LineProfiler
 
-def my_func():
+def f2(x):
     a = 0
-    for i in range(1000000):
+    for i in range(x):
         a += i
     return a
 
+def f1(x):
+    res = f2(x)
+    return res
+
 lp = LineProfiler()
 
-lp.add_function(my_func)
+lp.add_function(f2)
 
-lp.runcall(my_func)
+lp.runcall(f2, 1000000)
+lp.print_stats()
+###############################
 
+lp.add_function(f1)
+lp.runcall(f1, 1000000)
 lp.print_stats()
+