22 #include "../src/opcode_enum.h"
29 static int usage(
char * argv[]) {
30 fprintf(stderr,
"usage: %s [-f LOG_FILE] [-q] [-m] FILE [args...]\n", argv[0]);
34 static int help(
char * argv[]) {
37 "Generate callgrind-format trace files.\n"
40 " -f LOG_FILE Output callgrind-format data to LOG_FILE.\n"
41 " -q Do not print execution summary.\n"
42 " -m Interpret first argument as a module name rather than a file.\n"
44 " --help Show this help text.\n"
53 static size_t lastFrameCount = 0;
54 static size_t instrCounter = 0;
55 static size_t functionsEntered = 0;
57 static int moduleAsMain = 0;
68 static void time_diff(
struct timespec in_time,
char * buf) {
69 struct timespec outTime, diff;
70 clock_gettime(CLOCK_MONOTONIC, &outTime);
71 diff.tv_sec = outTime.tv_sec - in_time.tv_sec;
72 diff.tv_nsec = outTime.tv_nsec - in_time.tv_nsec;
73 if (diff.tv_nsec < 0) {
75 diff.tv_nsec += 1000000000L;
77 snprintf(buf,50,
"%lld%.9ld", (
long long)diff.tv_sec, diff.tv_nsec);
86 struct timespec in_time;
106 frameMetadata[lastFrameCount].count = instrCounter;
107 frameMetadata[lastFrameCount].source_obj = prev ? prev->
closure->
function : NULL;
109 clock_gettime(CLOCK_MONOTONIC, &frameMetadata[lastFrameCount].in_time);
112 size_t procFrame = lastFrameCount - 1;
114 if (procFrame == 0) {
120 time_diff(frameMetadata[procFrame].in_time, tmp);
121 KrkValue diffTime = krk_parse_int(tmp,strlen(tmp),10);
123 KrkValue totalTime = INTEGER_VAL(0);
124 krk_tableGet(AS_DICT(timeCache),OBJECT_VAL(frameMetadata[procFrame].target_obj),&totalTime);
125 krk_push(krk_operator_add(totalTime,diffTime));
134 if (!
krk_tableGet(AS_DICT(callCache),OBJECT_VAL(frameMetadata[procFrame].source_obj),&ndict)) {
137 krk_tableSet(AS_DICT(callCache),OBJECT_VAL(frameMetadata[procFrame].source_obj),ndict);
161 krk_push(krk_operator_add(AS_LIST(nlist)->values[0],INTEGER_VAL(1)));
162 AS_LIST(nlist)->values[0] =
krk_pop();
166 snprintf(tmp,50,
"%zu", instrCounter - frameMetadata[procFrame].count);
167 KrkValue diffCount = krk_parse_int(tmp,strlen(tmp),10);
169 krk_push(krk_operator_add(AS_LIST(nlist)->values[1],diffCount));
170 AS_LIST(nlist)->values[1] =
krk_pop();
173 time_diff(frameMetadata[procFrame].in_time, tmp);
174 KrkValue diffTime = krk_parse_int(tmp,strlen(tmp),10);
176 krk_push(krk_operator_add(AS_LIST(nlist)->values[2],diffTime));
177 AS_LIST(nlist)->values[2] =
krk_pop();
180 krk_tableGet(AS_DICT(timeCache),OBJECT_VAL(frameMetadata[procFrame].target_obj),&myTime);
181 krk_push(krk_operator_add(myTime,diffTime));
185 KrkValue parentTime = INTEGER_VAL(0);
186 krk_tableGet(AS_DICT(timeCache),OBJECT_VAL(frameMetadata[procFrame].source_obj),&parentTime);
187 krk_push(krk_operator_sub(parentTime,diffTime));
193 memset(&frameMetadata[procFrame], 0,
sizeof(
struct FrameMetadata));
200 if (!frame)
return KRK_DEBUGGER_QUIT;
216 krk_push(krk_operator_add(count, INTEGER_VAL(1)));
220 return KRK_DEBUGGER_STEP;
223 static void fprint_krk(FILE * f,
const char * fmt, ...) {
227 krk_pushStringBuilderFormatV(&sb, fmt, args);
228 fwrite(sb.bytes, sb.length, 1, f);
232 int main(
int argc,
char *argv[]) {
234 snprintf(outfile,1024,
"callgrind.out.%d",getpid());
237 while ((opt = getopt(argc, argv,
"+:f:qm-:")) != -1) {
240 snprintf(outfile,1024,
"%s", optarg);
250 fprintf(stderr,
"%s: unrocognized option '%c'\n", argv[0], optopt);
253 optarg = argv[optind]+1;
256 if (!strcmp(optarg,
"help")) {
259 fprintf(stderr,
"%s: unrecognized option: '--%s'\n", argv[0], optarg);
264 if (optind == argc) {
268 findInterpreter(argv);
272 KrkValue argList = addArgs(argc,argv);
294 if (!quiet) fprintf(stderr,
"== Executed ended by exception ==\n");
296 if (!quiet) fprintf(stderr,
"== Execution completed ==\n");
298 krk_callgrind_debuggerHook(NULL);
301 fprintf(stderr,
"%10zu total instruction%s\n", instrCounter, (instrCounter != 1) ?
"s" :
"");
302 fprintf(stderr,
"%10zu function%s calls\n", functionsEntered, (functionsEntered != 1) ?
"s" :
"");
305 FILE * f = fopen(outfile,
"w");
307 fprintf(stderr,
"%s: %s: %s\n", argv[0], outfile, strerror(errno));
311 fprintf(f,
"# callgrind format\n");
312 fprintf(f,
"creator: Kuroko\n");
313 fprintf(f,
"positions: line\n");
314 fprintf(f,
"events: instructions nanoseconds\n");
315 fprint_krk(f,
"cmd: %R\n", argList);
316 fprintf(f,
"summary: %zu ", instrCounter);
318 time_diff(frameMetadata[0].in_time, tmp);
319 fprintf(f,
"%s\n", tmp);
322 for (
size_t j = 0; j < AS_DICT(lineCache)->used; ++j) {
323 if (IS_KWARGS(AS_DICT(lineCache)->entries[j].key))
continue;
325 KrkCodeObject *
function = (
void*)AS_OBJECT(AS_DICT(lineCache)->entries[j].key);
326 KrkValue ndict = AS_DICT(lineCache)->entries[j].value;
328 fprintf(f,
"fl=%s\n", function->chunk.filename->chars);
329 fprintf(f,
"fn=%s@%p\n", function->qualname ? function->qualname->chars : function->name->chars, (
void*)
function);
332 krk_tableGet(AS_DICT(timeCache), OBJECT_VAL(
function), &timeValue);
333 if (!IS_NONE(timeValue)) {
335 fprint_krk(f,
"0 %R\n", timeValue);
338 for (
size_t k = 0; k < AS_DICT(ndict)->used; ++k) {
339 if (IS_KWARGS(AS_DICT(ndict)->entries[k].key))
continue;
340 fprint_krk(f,
"%R %R 0\n", AS_DICT(ndict)->entries[k].key, AS_DICT(ndict)->entries[k].value);
344 krk_tableGet(AS_DICT(callCache), OBJECT_VAL(
function), &cdict);
345 if (!IS_NONE(cdict)) {
346 for (
size_t l = 0; l < AS_DICT(cdict)->used; ++l) {
347 if (IS_KWARGS(AS_DICT(cdict)->entries[l].key))
continue;
349 KrkCodeObject * target = (
void*)AS_OBJECT(AS_TUPLE(AS_DICT(cdict)->entries[l].key)->values.values[0]);
350 KrkValue targetLine = AS_TUPLE(AS_DICT(cdict)->entries[l].key)->values.values[1];
351 KrkValue sourceLine = AS_TUPLE(AS_DICT(cdict)->entries[l].key)->values.values[2];
352 KrkValue totalCalls = AS_LIST(AS_DICT(cdict)->entries[l].value)->values[0];
353 KrkValue totalCost = AS_LIST(AS_DICT(cdict)->entries[l].value)->values[1];
354 KrkValue totalTime = AS_LIST(AS_DICT(cdict)->entries[l].value)->values[2];
356 fprintf(f,
"cfi=%s\n", target->
chunk.filename->
chars);
358 fprint_krk(f,
"calls=%R %R\n%R %R %R\n", totalCalls, targetLine, sourceLine, totalCost, totalTime);
Functions for debugging bytecode execution.
int krk_debug_registerCallback(KrkDebugCallback hook)
Register a debugger callback.
void krk_dumpTraceback(void)
If there is an active exception, print a traceback to stderr.
Top-level header with configuration macros.
Represents a managed call state in a VM thread.
size_t krk_lineNumber(KrkChunk *chunk, size_t offset)
Obtain the line number for a byte offset into a bytecode chunk.
KrkCodeObject * function
The codeobject containing the bytecode run when this function is called.
KrkChunk chunk
Bytecode data.
KrkString * name
Name of the function.
KrkString * qualname
The dotted name of the function.
KrkValue krk_dict_of(int argc, const KrkValue argv[], int hasKw)
Create a dict object.
KrkTable fields
Attributes table.
KrkValue krk_list_of(int argc, const KrkValue argv[], int hasKw)
Create a list object.
char * chars
UTF8 canonical data.
int krk_tableGet(KrkTable *table, KrkValue key, KrkValue *value)
Obtain the value associated with a key in a table.
int krk_tableSet(KrkTable *table, KrkValue key, KrkValue value)
Assign a value to a key in a table.
void krk_attachNamedValue(KrkTable *table, const char name[], KrkValue obj)
Attach a value to an attribute table.
unsigned int maximumCallDepth
Immutable sequence of arbitrary values.
KrkValueArray values
Stores the length, capacity, and actual values of the tuple.
KrkTuple * krk_newTuple(size_t length)
Create a new tuple.
void krk_freeVM(void)
Release resources from the VM.
void krk_initVM(int flags)
Initialize the VM at program startup.
Stack reference or primative value.
Inline flexible string array.
Utilities for creating native bindings.
KrkValue krk_discardStringBuilder(struct StringBuilder *sb)
Discard the contents of a string builder.
Core API for the bytecode virtual machine.
krk_threadLocal KrkThreadState krk_currentThread
Thread-local VM state.
int krk_importModule(KrkString *name, KrkString *runAs)
Load the dotted name name with the final element as runAs.
KrkValue krk_pop(void)
Pop the top of the stack.
KrkValue krk_runfile(const char *fileName, const char *fromFile)
Load and run a source file and return when execution completes.
void krk_push(KrkValue value)
Push a stack value.
KrkInstance * krk_startModule(const char *name)
Set up a new module object in the current thread.
#define KRK_CALL_FRAMES_MAX
Maximum depth of the call stack in managed-code function calls.
KrkValue krk_peek(int distance)
Peek down from the top of the stack.