mirror of
https://github.com/pocketpy/pocketpy
synced 2025-10-20 03:20:18 +00:00
Compare commits
7 Commits
3c36b30f04
...
abf96a7128
Author | SHA1 | Date | |
---|---|---|---|
|
abf96a7128 | ||
|
2b7772d371 | ||
|
af47a118f7 | ||
|
03ee6563c8 | ||
|
4633ca7242 | ||
|
bc51c5e19d | ||
|
169e825197 |
3
.gitignore
vendored
3
.gitignore
vendored
@ -40,4 +40,5 @@ docs/C-API/functions.md
|
|||||||
|
|
||||||
|
|
||||||
cmake-build-*
|
cmake-build-*
|
||||||
tmp/
|
tmp/
|
||||||
|
profiler_report.json
|
||||||
|
@ -24,5 +24,6 @@ void LineProfiler__begin(LineProfiler* self);
|
|||||||
void LineProfiler__tracefunc_line(LineProfiler* self, py_Frame* frame);
|
void LineProfiler__tracefunc_line(LineProfiler* self, py_Frame* frame);
|
||||||
void LineProfiler__end(LineProfiler* self);
|
void LineProfiler__end(LineProfiler* self);
|
||||||
void LineProfiler__reset(LineProfiler* self);
|
void LineProfiler__reset(LineProfiler* self);
|
||||||
|
c11_string* LineProfiler__get_report(LineProfiler* self);
|
||||||
|
|
||||||
void LineProfiler__tracefunc(py_Frame* frame, enum py_TraceEvent event);
|
void LineProfiler_tracefunc(py_Frame* frame, enum py_TraceEvent event);
|
@ -701,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;
|
PK_API bool py_pickle_dumps(py_Ref val) PY_RAISE PY_RETURN;
|
||||||
/// Python equivalent to `pickle.loads(val)`.
|
/// Python equivalent to `pickle.loads(val)`.
|
||||||
PK_API bool py_pickle_loads(const unsigned char* data, int size) PY_RAISE PY_RETURN;
|
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 *************/
|
/************* Unchecked Functions *************/
|
||||||
|
|
||||||
PK_API py_ObjectRef py_tuple_data(py_Ref self);
|
PK_API py_ObjectRef py_tuple_data(py_Ref self);
|
||||||
|
@ -28,15 +28,19 @@ def currentvm() -> int:
|
|||||||
|
|
||||||
|
|
||||||
def watchdog_begin(timeout: int):
|
def watchdog_begin(timeout: int):
|
||||||
"""
|
"""Begin the watchdog with `timeout` in milliseconds.
|
||||||
Begin the watchdog with `timeout` in milliseconds.
|
|
||||||
`PK_ENABLE_WATCHDOG` must be defined to `1` to use this feature.
|
`PK_ENABLE_WATCHDOG` must be defined to `1` to use this feature.
|
||||||
You need to call `watchdog_end()` later.
|
You need to call `watchdog_end()` later.
|
||||||
If `timeout` is reached, `TimeoutError` will be raised.
|
If `timeout` is reached, `TimeoutError` will be raised.
|
||||||
"""
|
"""
|
||||||
def watchdog_end():
|
def watchdog_end() -> None:
|
||||||
"""Reset the watchdog."""
|
"""End the watchdog after a call to `watchdog_begin()`."""
|
||||||
|
|
||||||
|
def profiler_begin() -> None: ...
|
||||||
|
def profiler_end() -> None: ...
|
||||||
|
def profiler_reset() -> None: ...
|
||||||
|
def profiler_report() -> dict[str, list[list]]: ...
|
||||||
|
|
||||||
class ComputeThread:
|
class ComputeThread:
|
||||||
def __init__(self, vm_index: Literal[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]): ...
|
def __init__(self, vm_index: Literal[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]): ...
|
||||||
|
@ -1,62 +0,0 @@
|
|||||||
#include "pocketpy/interpreter/line_profiler.h"
|
|
||||||
#include <assert.h>
|
|
||||||
|
|
||||||
void LineProfiler__ctor(LineProfiler* self) {
|
|
||||||
c11_smallmap_p2i__ctor(&self->records);
|
|
||||||
self->prev_loc.src = NULL;
|
|
||||||
self->prev_time = 0;
|
|
||||||
self->enabled = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
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) {
|
|
||||||
assert(!self->enabled);
|
|
||||||
self->prev_loc.src = NULL;
|
|
||||||
self->prev_time = 0;
|
|
||||||
self->enabled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void LineProfiler__tracefunc_line(LineProfiler* self, py_Frame* frame) {
|
|
||||||
assert(self->enabled);
|
|
||||||
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) {
|
|
||||||
assert(self->enabled);
|
|
||||||
if(self->prev_loc.src != NULL) {
|
|
||||||
LineRecord* line = LineProfiler__get_record(self, self->prev_loc);
|
|
||||||
line->hits++;
|
|
||||||
line->time += clock() - self->prev_time;
|
|
||||||
}
|
|
||||||
self->enabled = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void LineProfiler__reset(LineProfiler* self) {
|
|
||||||
LineProfiler__dtor(self);
|
|
||||||
LineProfiler__ctor(self);
|
|
||||||
}
|
|
110
src/interpreter/line_profiler.c
Normal file
110
src/interpreter/line_profiler.c
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
#include "pocketpy/common/sstream.h"
|
||||||
|
#include "pocketpy/interpreter/line_profiler.h"
|
||||||
|
#include "pocketpy/objects/sourcedata.h"
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
void LineProfiler__ctor(LineProfiler* self) {
|
||||||
|
c11_smallmap_p2i__ctor(&self->records);
|
||||||
|
self->prev_loc.src = NULL;
|
||||||
|
self->prev_time = 0;
|
||||||
|
self->enabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LineProfiler__dtor(LineProfiler* self) {
|
||||||
|
if(self->prev_loc.src != NULL) PK_DECREF(self->prev_loc.src);
|
||||||
|
for(int i = 0; i < self->records.length; i++) {
|
||||||
|
c11_smallmap_p2i_KV kv = c11__getitem(c11_smallmap_p2i_KV, &self->records, i);
|
||||||
|
SourceData_ src = (SourceData_)kv.key;
|
||||||
|
PK_DECREF(src);
|
||||||
|
PK_FREE((void*)kv.value);
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
PK_INCREF(loc.src);
|
||||||
|
}
|
||||||
|
return &lines[loc.lineno];
|
||||||
|
}
|
||||||
|
|
||||||
|
void LineProfiler__begin(LineProfiler* self) {
|
||||||
|
assert(!self->enabled);
|
||||||
|
self->prev_loc.src = NULL;
|
||||||
|
self->prev_time = 0;
|
||||||
|
self->enabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LineProfiler__tracefunc_line(LineProfiler* self, py_Frame* frame) {
|
||||||
|
assert(self->enabled);
|
||||||
|
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;
|
||||||
|
PK_DECREF(self->prev_loc.src);
|
||||||
|
}
|
||||||
|
self->prev_loc = Frame__source_location(frame);
|
||||||
|
PK_INCREF(self->prev_loc.src);
|
||||||
|
self->prev_time = now;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LineProfiler__end(LineProfiler* self) {
|
||||||
|
assert(self->enabled);
|
||||||
|
if(self->prev_loc.src != NULL) {
|
||||||
|
LineRecord* line = LineProfiler__get_record(self, self->prev_loc);
|
||||||
|
line->hits++;
|
||||||
|
line->time += clock() - self->prev_time;
|
||||||
|
}
|
||||||
|
self->enabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LineProfiler__reset(LineProfiler* self) {
|
||||||
|
LineProfiler__dtor(self);
|
||||||
|
LineProfiler__ctor(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);
|
||||||
|
SourceData_ src = (SourceData_)kv.key;
|
||||||
|
int line_record_length = src->line_starts.length + 1;
|
||||||
|
c11_sv src_name = c11_string__sv(src->filename);
|
||||||
|
c11_sbuf__write_quoted(&sbuf, src_name, '"');
|
||||||
|
c11_sbuf__write_cstr(&sbuf, ": [");
|
||||||
|
LineRecord* lines = (LineRecord*)kv.value;
|
||||||
|
bool is_first = true;
|
||||||
|
for(int j = 1; j < line_record_length; j++) {
|
||||||
|
// [<j>, <hits>, <time>]
|
||||||
|
if(lines[j].hits == 0 && lines[j].time == 0) continue;
|
||||||
|
if(!is_first) c11_sbuf__write_cstr(&sbuf, ", ");
|
||||||
|
c11_sbuf__write_cstr(&sbuf, "[");
|
||||||
|
c11_sbuf__write_int(&sbuf, j);
|
||||||
|
c11_sbuf__write_cstr(&sbuf, ", ");
|
||||||
|
c11_sbuf__write_i64(&sbuf, lines[j].hits);
|
||||||
|
c11_sbuf__write_cstr(&sbuf, ", ");
|
||||||
|
c11_sbuf__write_i64(&sbuf, lines[j].time);
|
||||||
|
c11_sbuf__write_cstr(&sbuf, "]");
|
||||||
|
is_first = false;
|
||||||
|
}
|
||||||
|
c11_sbuf__write_cstr(&sbuf, "]");
|
||||||
|
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);
|
||||||
|
}
|
@ -34,9 +34,36 @@ static void pk_default_flush() { fflush(stdout); }
|
|||||||
|
|
||||||
static int pk_default_getchr() { return getchar(); }
|
static int pk_default_getchr() { return getchar(); }
|
||||||
|
|
||||||
void LineProfiler__tracefunc(py_Frame* frame, enum py_TraceEvent event) {
|
void py_profiler_begin() {
|
||||||
LineProfiler* self = &pk_current_vm->line_profiler;
|
LineProfiler* lp = &pk_current_vm->line_profiler;
|
||||||
if(self->enabled && event == TRACE_EVENT_LINE) { LineProfiler__tracefunc_line(self, frame); }
|
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) {
|
static int BinTree__cmp_cstr(void* lhs, void* rhs) {
|
||||||
@ -234,9 +261,9 @@ void VM__ctor(VM* self) {
|
|||||||
pk__add_module_unicodedata();
|
pk__add_module_unicodedata();
|
||||||
|
|
||||||
pk__add_module_conio();
|
pk__add_module_conio();
|
||||||
pk__add_module_lz4(); // optional
|
pk__add_module_lz4(); // optional
|
||||||
pk__add_module_libhv(); // optional
|
pk__add_module_libhv(); // optional
|
||||||
pk__add_module_cute_png(); // optional
|
pk__add_module_cute_png(); // optional
|
||||||
pk__add_module_pkpy();
|
pk__add_module_pkpy();
|
||||||
|
|
||||||
// add python builtins
|
// add python builtins
|
||||||
|
@ -458,7 +458,7 @@ static void pk_ComputeThread__register(py_Ref mod) {
|
|||||||
py_bindmethod(type, "eval", ComputeThread_eval);
|
py_bindmethod(type, "eval", ComputeThread_eval);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // PK_ENABLE_THREADS
|
#endif // PK_ENABLE_THREADS
|
||||||
|
|
||||||
static void pkpy_configmacros_add(py_Ref dict, const char* key, int val) {
|
static void pkpy_configmacros_add(py_Ref dict, const char* key, int val) {
|
||||||
assert(dict->type == tp_dict);
|
assert(dict->type == tp_dict);
|
||||||
@ -467,6 +467,42 @@ static void pkpy_configmacros_add(py_Ref dict, const char* key, int val) {
|
|||||||
py_dict_setitem_by_str(dict, key, &tmp);
|
py_dict_setitem_by_str(dict, key, &tmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool pkpy_profiler_begin(int argc, py_Ref argv) {
|
||||||
|
PY_CHECK_ARGC(0);
|
||||||
|
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());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool pkpy_profiler_end(int argc, py_Ref argv) {
|
||||||
|
PY_CHECK_ARGC(0);
|
||||||
|
LineProfiler__end(&pk_current_vm->line_profiler);
|
||||||
|
py_newnone(py_retval());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool pkpy_profiler_reset(int argc, py_Ref argv) {
|
||||||
|
PY_CHECK_ARGC(0);
|
||||||
|
LineProfiler__reset(&pk_current_vm->line_profiler);
|
||||||
|
py_newnone(py_retval());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool pkpy_profiler_report(int argc, py_Ref argv) {
|
||||||
|
PY_CHECK_ARGC(0);
|
||||||
|
LineProfiler* lp = &pk_current_vm->line_profiler;
|
||||||
|
if(lp->enabled) LineProfiler__end(lp);
|
||||||
|
c11_string* report = LineProfiler__get_report(lp);
|
||||||
|
bool ok = py_json_loads(report->data);
|
||||||
|
c11_string__delete(report);
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
void pk__add_module_pkpy() {
|
void pk__add_module_pkpy() {
|
||||||
py_Ref mod = py_newmodule("pkpy");
|
py_Ref mod = py_newmodule("pkpy");
|
||||||
|
|
||||||
@ -516,6 +552,11 @@ void pk__add_module_pkpy() {
|
|||||||
pk_ComputeThread__register(mod);
|
pk_ComputeThread__register(mod);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
py_bindfunc(mod, "profiler_begin", pkpy_profiler_begin);
|
||||||
|
py_bindfunc(mod, "profiler_end", pkpy_profiler_end);
|
||||||
|
py_bindfunc(mod, "profiler_reset", pkpy_profiler_reset);
|
||||||
|
py_bindfunc(mod, "profiler_report", pkpy_profiler_report);
|
||||||
|
|
||||||
py_Ref configmacros = py_emplacedict(mod, py_name("configmacros"));
|
py_Ref configmacros = py_emplacedict(mod, py_name("configmacros"));
|
||||||
py_newdict(configmacros);
|
py_newdict(configmacros);
|
||||||
pkpy_configmacros_add(configmacros, "PK_ENABLE_OS", PK_ENABLE_OS);
|
pkpy_configmacros_add(configmacros, "PK_ENABLE_OS", PK_ENABLE_OS);
|
||||||
|
21
src2/main.c
21
src2/main.c
@ -25,7 +25,6 @@ static char* read_file(const char* path) {
|
|||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
// void LineProfiler__tracefunc(py_Frame* frame, enum py_TraceEvent event);
|
|
||||||
static char buf[2048];
|
static char buf[2048];
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
int main(int argc, char** argv) {
|
||||||
@ -52,10 +51,8 @@ int main(int argc, char** argv) {
|
|||||||
py_initialize();
|
py_initialize();
|
||||||
py_sys_setargv(argc, argv);
|
py_sys_setargv(argc, argv);
|
||||||
|
|
||||||
assert(!profile); // not implemented yet
|
|
||||||
// if(profile) py_sys_settrace(LineProfiler__tracefunc, true);
|
|
||||||
|
|
||||||
if(filename == NULL) {
|
if(filename == NULL) {
|
||||||
|
if(profile) printf("Warning: --profile is ignored in REPL mode.\n");
|
||||||
printf("pocketpy " PK_VERSION " (" __DATE__ ", " __TIME__ ") ");
|
printf("pocketpy " PK_VERSION " (" __DATE__ ", " __TIME__ ") ");
|
||||||
printf("[%d bit] on %s", (int)(sizeof(void*) * 8), PY_SYS_PLATFORM_STRING);
|
printf("[%d bit] on %s", (int)(sizeof(void*) * 8), PY_SYS_PLATFORM_STRING);
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
@ -81,9 +78,23 @@ int main(int argc, char** argv) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
if(profile) py_profiler_begin();
|
||||||
char* source = read_file(filename);
|
char* source = read_file(filename);
|
||||||
if(source) {
|
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);
|
PK_FREE(source);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user