Ajla 0.1.0
[ajla.git] / profile.c
blob6114a7c787d358f24de637cb06f260811be206e1
1 /*
2 * Copyright (C) 2024 Mikulas Patocka
4 * This file is part of Ajla.
6 * Ajla is free software: you can redistribute it and/or modify it under the
7 * terms of the GNU General Public License as published by the Free Software
8 * Foundation, either version 3 of the License, or (at your option) any later
9 * version.
11 * Ajla is distributed in the hope that it will be useful, but WITHOUT ANY
12 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
13 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License along with
16 * Ajla. If not, see <https://www.gnu.org/licenses/>.
19 #include "ajla.h"
21 #include "str.h"
22 #include "thread.h"
23 #include "util.h"
24 #include "os.h"
26 #include "profile.h"
28 uchar_efficient_t profiling = 0;
29 uchar_efficient_t profiling_escapes = 0;
31 struct profile_data {
32 const char *function_name;
33 profile_counter_t profiling_counter;
34 profile_counter_t call_counter;
37 static struct profile_data *pd;
38 static size_t pd_len;
40 tls_decl(ajla_time_t, profiler_time);
42 void profile_unblock(void)
44 ajla_time_t now = os_time_monotonic();
45 tls_set(ajla_time_t, profiler_time, now);
48 profile_counter_t profile_sample(void)
50 profile_counter_t retval;
51 ajla_time_t now = os_time_monotonic();
52 ajla_time_t us = tls_get(ajla_time_t, profiler_time);
53 retval = ((now - us) + 500) / 1000;
54 tls_set(ajla_time_t, profiler_time, now);
55 return retval;
58 void profile_collect(const char *function_name, profile_counter_t profiling_counter, profile_counter_t call_counter)
60 struct profile_data p;
61 p.function_name = str_dup(function_name, -1, NULL);
62 p.profiling_counter = profiling_counter;
63 p.call_counter = call_counter;
64 array_add(struct profile_data, &pd, &pd_len, p);
67 static int profile_cmp(const void *p1, const void *p2)
69 const struct profile_data *q1 = p1;
70 const struct profile_data *q2 = p2;
71 if (q1->profiling_counter < q2->profiling_counter) return -1;
72 if (q1->profiling_counter > q2->profiling_counter) return 1;
73 if (q1->call_counter < q2->call_counter) return -1;
74 if (q1->call_counter > q2->call_counter) return 1;
75 return strcmp(q1->function_name, q2->function_name);
78 static void profile_print(void)
80 size_t i;
81 qsort(pd, pd_len, sizeof(struct profile_data), profile_cmp);
82 for (i = 0; i < pd_len; i++) {
83 debug("%-30s %"PRIuMAX" %"PRIuMAX"", pd[i].function_name, (uintmax_t)pd[i].profiling_counter, (uintmax_t)pd[i].call_counter);
84 mem_free(pd[i].function_name);
86 mem_free(pd);
90 struct profile_escape_data {
91 const char *function_name;
92 profile_counter_t profiling_counter;
93 unsigned line;
94 code_t code;
95 unsigned short compile_line;
98 static struct profile_escape_data *ped;
99 static size_t ped_len;
101 void profile_escape_collect(const char *function_name, profile_counter_t profiling_counter, unsigned line, code_t code, unsigned short compile_line)
103 struct profile_escape_data pe;
104 if ((code % OPCODE_MODE_MULT) == OPCODE_CHECKPOINT)
105 return;
106 pe.function_name = str_dup(function_name, -1, NULL);
107 pe.profiling_counter = profiling_counter;
108 pe.line = line;
109 pe.code = code;
110 pe.compile_line = compile_line;
111 array_add(struct profile_escape_data, &ped, &ped_len, pe);
114 static int profile_escape_cmp(const void *p1, const void *p2)
116 int r;
117 const struct profile_escape_data *q1 = p1;
118 const struct profile_escape_data *q2 = p2;
119 if (q1->profiling_counter < q2->profiling_counter) return -1;
120 if (q1->profiling_counter > q2->profiling_counter) return 1;
121 r = strcmp(q1->function_name, q2->function_name);
122 if (r)
123 return r;
124 if (q1->line < q2->line) return -1;
125 if (q1->line > q2->line) return 1;
126 if (q1->code < q2->code) return -1;
127 if (q1->code > q2->code) return 1;
128 if (q1->compile_line < q2->compile_line) return -1;
129 if (q1->compile_line > q2->compile_line) return 1;
130 return 0;
133 static void profile_escape_print(void)
135 size_t i;
136 qsort(ped, ped_len, sizeof(struct profile_escape_data), profile_escape_cmp);
137 for (i = 0; i < ped_len; i++) {
138 debug("%30s:%-6u %-10"PRIuMAX" %s,%u", ped[i].function_name, ped[i].line, (uintmax_t)ped[i].profiling_counter, decode_opcode(ped[i].code, false), ped[i].compile_line);
139 mem_free(ped[i].function_name);
141 mem_free(ped);
145 bool function_enable_profile(const char *option, size_t l)
147 if (!option)
148 profiling = profiling_escapes = 1;
149 else if (l == 8 && !strncmp(option, "function", l))
150 profiling = 1;
151 else if (l == 6 && !strncmp(option, "escape", l))
152 profiling_escapes = 1;
153 else
154 return false;
155 return true;
158 void profile_init(void)
160 if (profiling) {
161 tls_init(ajla_time_t, profiler_time);
162 array_init(struct profile_data, &pd, &pd_len);
164 if (profiling_escapes) {
165 array_init(struct profile_escape_data, &ped, &ped_len);
169 void profile_done(void)
171 if (profiling) {
172 profile_print();
173 tls_done(ajla_time_t, profiler_time);
175 if (profiling_escapes) {
176 profile_escape_print();