/src/opensips/profiling.c
Line | Count | Source |
1 | | /* |
2 | | * Profiling hooks for external instrumentation |
3 | | * |
4 | | * Copyright (C) 2026 OpenSIPS Project |
5 | | * |
6 | | * opensips is free software; you can redistribute it and/or modify |
7 | | * it under the terms of the GNU General Public License as published by |
8 | | * the Free Software Foundation; either version 2 of the License, or |
9 | | * (at your option) any later version. |
10 | | * |
11 | | * opensips is distributed in the hope that it will be useful, |
12 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | | * GNU General Public License for more details. |
15 | | * |
16 | | * You should have received a copy of the GNU General Public License |
17 | | * along with this program; if not, write to the Free Software |
18 | | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, |
19 | | * MA 02110-1301, USA. |
20 | | * |
21 | | */ |
22 | | |
23 | | #include "profiling.h" |
24 | | #include "dprint.h" |
25 | | #include "evi/evi_modules.h" |
26 | | #include "evi/evi_params.h" |
27 | | #include "parser/msg_parser.h" |
28 | | #include "pt.h" |
29 | | #include <string.h> |
30 | | #include <sys/time.h> |
31 | | |
32 | | profiling_handlers_t *profiling_handlers; |
33 | | |
34 | | typedef struct profiling_event_handle { |
35 | | event_id_t id; |
36 | | evi_params_p params; |
37 | | evi_param_p sec_p; |
38 | | evi_param_p usec_p; |
39 | | evi_param_p session_p; |
40 | | evi_param_p verb_p; |
41 | | evi_param_p name_p; |
42 | | evi_param_p type_p; |
43 | | evi_param_p depth_p; |
44 | | evi_param_p file_p; |
45 | | evi_param_p line_p; |
46 | | evi_param_p status_p; |
47 | | } profiling_event_handle_t; |
48 | | |
49 | | static str ev_name_script = str_init("E_PROFILING_SCRIPT"); |
50 | | static str ev_name_proc = str_init("E_PROFILING_PROC"); |
51 | | static str ev_param_sec = str_init("sec"); |
52 | | static str ev_param_usec = str_init("usec"); |
53 | | static str ev_param_session = str_init("session"); |
54 | | static str ev_param_verb = str_init("verb"); |
55 | | static str ev_param_name = str_init("name"); |
56 | | static str ev_param_type = str_init("type"); |
57 | | static str ev_param_depth = str_init("depth"); |
58 | | static str ev_param_file = str_init("file"); |
59 | | static str ev_param_line = str_init("line"); |
60 | | static str ev_param_status = str_init("status"); |
61 | | |
62 | | static profiling_event_handle_t profiling_events[2]; |
63 | | |
64 | | uint16_t proc_depth = 0; |
65 | | profiling_proc_pending_t |
66 | | profiling_proc_pending_stack[PROFILING_PROC_STACK_MAX]; |
67 | | uint16_t profiling_proc_pending_no = 0; |
68 | | |
69 | | |
70 | | static int profiling_data_type_to_idx(int data_type) |
71 | 0 | { |
72 | 0 | switch (data_type) { |
73 | 0 | case PROFILING_DATA_TYPE_SCRIPT: |
74 | 0 | return 0; |
75 | 0 | case PROFILING_DATA_TYPE_PROC: |
76 | 0 | return 1; |
77 | 0 | default: |
78 | 0 | return -1; |
79 | 0 | } |
80 | 0 | } |
81 | | |
82 | | static int init_profiling_event(profiling_event_handle_t *ev, str *ev_name) |
83 | 0 | { |
84 | 0 | ev->id = evi_publish_event(*ev_name); |
85 | 0 | if (ev->id == EVI_ERROR) { |
86 | 0 | LM_ERR("cannot register '%.*s' event\n", ev_name->len, ev_name->s); |
87 | 0 | return -1; |
88 | 0 | } |
89 | | |
90 | 0 | ev->params = evi_get_params(); |
91 | 0 | if (!ev->params) { |
92 | 0 | LM_ERR("cannot create params for '%.*s'\n", ev_name->len, ev_name->s); |
93 | 0 | return -1; |
94 | 0 | } |
95 | 0 | ev->params->flags &= ~EVI_FREE_LIST; |
96 | |
|
97 | 0 | ev->sec_p = evi_param_create(ev->params, &ev_param_sec); |
98 | 0 | ev->usec_p = evi_param_create(ev->params, &ev_param_usec); |
99 | 0 | ev->session_p = evi_param_create(ev->params, &ev_param_session); |
100 | 0 | ev->verb_p = evi_param_create(ev->params, &ev_param_verb); |
101 | 0 | ev->name_p = evi_param_create(ev->params, &ev_param_name); |
102 | 0 | ev->type_p = evi_param_create(ev->params, &ev_param_type); |
103 | 0 | ev->depth_p = evi_param_create(ev->params, &ev_param_depth); |
104 | 0 | ev->file_p = evi_param_create(ev->params, &ev_param_file); |
105 | 0 | ev->line_p = evi_param_create(ev->params, &ev_param_line); |
106 | 0 | ev->status_p = evi_param_create(ev->params, &ev_param_status); |
107 | 0 | if (!ev->sec_p || !ev->usec_p || !ev->session_p || !ev->verb_p || |
108 | 0 | !ev->name_p || !ev->type_p || !ev->depth_p || !ev->file_p || |
109 | 0 | !ev->line_p || !ev->status_p) { |
110 | 0 | LM_ERR("cannot create params for '%.*s'\n", ev_name->len, ev_name->s); |
111 | 0 | return -1; |
112 | 0 | } |
113 | | |
114 | 0 | return 0; |
115 | 0 | } |
116 | | |
117 | | static inline void profiling_raise_event(int data_type, char *verb, |
118 | | const char *name, int type, int depth, const char *file, int line, |
119 | | int *status, void *payload) |
120 | 0 | { |
121 | 0 | int idx; |
122 | 0 | profiling_event_handle_t *ev; |
123 | 0 | struct timeval tv; |
124 | 0 | int sec; |
125 | 0 | int usec; |
126 | 0 | int session = 0; |
127 | 0 | struct sip_msg *msg; |
128 | 0 | str verb_s, name_s, file_s; |
129 | |
|
130 | 0 | idx = profiling_data_type_to_idx(data_type); |
131 | 0 | if (idx < 0) |
132 | 0 | return; |
133 | | |
134 | 0 | ev = &profiling_events[idx]; |
135 | 0 | if (ev->id == EVI_ERROR || !ev->params) |
136 | 0 | return; |
137 | | |
138 | 0 | if (!evi_probe_event(ev->id)) |
139 | 0 | return; |
140 | | |
141 | 0 | if (!name) |
142 | 0 | name = "<root>"; |
143 | |
|
144 | 0 | if (!verb) |
145 | 0 | verb = ""; |
146 | 0 | verb_s.s = verb; |
147 | 0 | verb_s.len = strlen(verb); |
148 | |
|
149 | 0 | if (gettimeofday(&tv, NULL) < 0) { |
150 | 0 | LM_ERR("failed to get current time\n"); |
151 | 0 | return; |
152 | 0 | } |
153 | 0 | sec = (int)tv.tv_sec; |
154 | 0 | usec = (int)tv.tv_usec; |
155 | |
|
156 | 0 | switch (data_type) { |
157 | 0 | case PROFILING_DATA_TYPE_SCRIPT: |
158 | 0 | msg = (struct sip_msg *)payload; |
159 | 0 | if (msg && msg != FAKED_REPLY) |
160 | 0 | session = (int)msg->id; |
161 | 0 | break; |
162 | 0 | case PROFILING_DATA_TYPE_PROC: |
163 | 0 | session = my_pid(); |
164 | 0 | break; |
165 | 0 | } |
166 | | |
167 | 0 | name_s.s = (char *)name; |
168 | 0 | name_s.len = strlen(name); |
169 | |
|
170 | 0 | if (file) { |
171 | 0 | file_s.s = (char *)file; |
172 | 0 | file_s.len = strlen(file); |
173 | 0 | } |
174 | | // This is for debugging only |
175 | | //LM_DBG("raising profiling even < %s | %s >\n", verb, name); |
176 | |
|
177 | 0 | set_proc_log_level(L_ERR); |
178 | |
|
179 | 0 | if (evi_param_set_int(ev->sec_p, &sec) < 0 || |
180 | 0 | evi_param_set_int(ev->usec_p, &usec) < 0 || |
181 | 0 | evi_param_set_int(ev->session_p, &session) < 0 || |
182 | 0 | evi_param_set_str(ev->verb_p, &verb_s) < 0 || |
183 | 0 | evi_param_set_str(ev->name_p, &name_s) < 0 || |
184 | 0 | evi_param_set_int(ev->type_p, &type) < 0 || |
185 | 0 | evi_param_set_int(ev->depth_p, &depth) < 0 ) { |
186 | 0 | LM_ERR("cannot populate profiling event params 1\n"); |
187 | 0 | goto error; |
188 | 0 | } |
189 | | |
190 | 0 | if (file) { |
191 | 0 | if (evi_param_set_str(ev->file_p, &file_s) < 0 || |
192 | 0 | evi_param_set_int(ev->line_p, &line) < 0 ) { |
193 | 0 | LM_ERR("cannot populate profiling event params 2\n"); |
194 | 0 | goto error; |
195 | 0 | } |
196 | 0 | } else { |
197 | 0 | evi_param_reset(ev->file_p); |
198 | 0 | evi_param_reset(ev->line_p); |
199 | 0 | } |
200 | | |
201 | 0 | if (status) { |
202 | 0 | if (evi_param_set_int(ev->status_p, status) < 0) { |
203 | 0 | LM_ERR("cannot populate profiling event params 3\n"); |
204 | 0 | goto error; |
205 | 0 | } |
206 | 0 | } else { |
207 | 0 | evi_param_reset(ev->status_p); |
208 | 0 | } |
209 | | |
210 | 0 | if (evi_raise_event(ev->id, ev->params) < 0) |
211 | 0 | LM_ERR("cannot raise profiling event\n"); |
212 | |
|
213 | 0 | error: |
214 | 0 | reset_proc_log_level(); |
215 | 0 | return; |
216 | 0 | } |
217 | | |
218 | | static void profiling_event_on_start(int data_type, const char *name, |
219 | | int subtype, int depth, void *payload) |
220 | 0 | { |
221 | 0 | profiling_raise_event(data_type, "start", name, subtype, depth, NULL, 0, |
222 | 0 | NULL, payload); |
223 | 0 | } |
224 | | |
225 | | static void profiling_event_on_end(int data_type, const char *name, |
226 | | int subtype, int depth, int status, void *payload) |
227 | 0 | { |
228 | 0 | profiling_raise_event(data_type, "end", name, subtype, depth, NULL, 0, |
229 | 0 | &status, payload); |
230 | 0 | } |
231 | | |
232 | | static void profiling_event_on_enter(int data_type, const char *name, |
233 | | int subtype, int depth, const char *file, int line, void *payload) |
234 | 0 | { |
235 | 0 | profiling_raise_event(data_type, "enter", name, subtype, depth, file, |
236 | 0 | line, NULL, payload); |
237 | 0 | } |
238 | | |
239 | | static void profiling_event_on_exit(int data_type, const char *name, |
240 | | int subtype, int depth, const char *file, int line, int status, |
241 | | void *payload) |
242 | 0 | { |
243 | 0 | profiling_raise_event(data_type, "exit", name, subtype, depth, file, line, |
244 | 0 | &status, payload); |
245 | 0 | } |
246 | | |
247 | | static profiling_handlers_t profiling_event_handler = { |
248 | | .name = "event", |
249 | | .accepted_data_types = PROFILING_DATA_TYPE_SCRIPT | |
250 | | PROFILING_DATA_TYPE_PROC, |
251 | | .next = NULL, |
252 | | .on_start = profiling_event_on_start, |
253 | | .on_end = profiling_event_on_end, |
254 | | .on_enter = profiling_event_on_enter, |
255 | | .on_exit = profiling_event_on_exit, |
256 | | .get_ctx = NULL, |
257 | | .set_ctx = NULL, |
258 | | }; |
259 | | |
260 | | int init_profiling(void) |
261 | 0 | { |
262 | 0 | memset(profiling_events, 0, sizeof(profiling_events)); |
263 | 0 | profiling_events[0].id = EVI_ERROR; |
264 | 0 | profiling_events[1].id = EVI_ERROR; |
265 | |
|
266 | 0 | if (init_profiling_event(&profiling_events[0], &ev_name_script) < 0) |
267 | 0 | return -1; |
268 | 0 | if (init_profiling_event(&profiling_events[1], &ev_name_proc) < 0) |
269 | 0 | return -1; |
270 | | |
271 | 0 | if (register_profiling_handler(&profiling_event_handler) < 0) { |
272 | 0 | LM_ERR("failed to register profiling event handler\n"); |
273 | 0 | return -1; |
274 | 0 | } |
275 | | |
276 | 0 | return 0; |
277 | 0 | } |
278 | | |
279 | | int register_profiling_handler(profiling_handlers_t *handlers) |
280 | 0 | { |
281 | 0 | profiling_handlers_t *it; |
282 | |
|
283 | 0 | if (!handlers) |
284 | 0 | return -1; |
285 | | |
286 | 0 | for (it = profiling_handlers; it; it = it->next) { |
287 | 0 | if (it == handlers) |
288 | 0 | return 0; |
289 | 0 | } |
290 | | |
291 | 0 | handlers->next = profiling_handlers; |
292 | 0 | profiling_handlers = handlers; |
293 | 0 | return 0; |
294 | 0 | } |
295 | | |
296 | | void unregister_profiling_handler(profiling_handlers_t *handlers) |
297 | 0 | { |
298 | 0 | profiling_handlers_t **it; |
299 | |
|
300 | 0 | for (it = &profiling_handlers; *it; it = &(*it)->next) { |
301 | 0 | if (*it == handlers) { |
302 | 0 | *it = handlers->next; |
303 | | handlers->next = NULL; |
304 | 0 | return; |
305 | 0 | } |
306 | 0 | } |
307 | 0 | } |