Coverage Report

Created: 2026-08-13 06:11

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}