Coverage Report

Created: 2026-09-03 07:04

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/opensips/profiling.h
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, MA 02110-1301, USA.
19
 *
20
 */
21
22
#ifndef PROFILING_H
23
#define PROFILING_H
24
25
#include <stddef.h>
26
#include <stdint.h>
27
#include "pt.h"
28
29
struct sip_msg;
30
31
typedef struct profiling_ctx {
32
  uint8_t trace_id[16];
33
  uint8_t span_id[8];
34
  uint8_t trace_flags;
35
  uint64_t start_system_ns;
36
  uint64_t start_steady_ns;
37
  uint8_t has_start_time;
38
} profiling_ctx_t;
39
40
0
#define PROFILING_DATA_TYPE_SCRIPT (1u << 0)
41
0
#define PROFILING_DATA_TYPE_PROC   (1u << 1)
42
43
typedef struct profiling_handlers {
44
  const char *name;
45
  uint32_t accepted_data_types;
46
  struct profiling_handlers *next;
47
48
  void (*on_start)(int data_type, const char *name, int subtype, int depth,
49
    void *payload);
50
  void (*on_end)(int data_type, const char *name, int subtype, int depth,
51
    int status, void *payload);
52
  void (*on_enter)(int data_type, const char *name, int subtype, int depth,
53
    const char *file, int line, void *payload);
54
  void (*on_exit)(int data_type, const char *name, int subtype, int depth,
55
    const char *file, int line, int status, void *payload);
56
  int (*get_ctx)(profiling_ctx_t *ctx);
57
  int (*set_ctx)(const profiling_ctx_t *ctx);
58
} profiling_handlers_t;
59
60
extern profiling_handlers_t *profiling_handlers;
61
62
int init_profiling(void);
63
int register_profiling_handler(profiling_handlers_t *handlers);
64
void unregister_profiling_handler(profiling_handlers_t *handlers);
65
66
static inline int profiling_enabled(unsigned int data_type_mask)
67
0
{
68
0
  profiling_handlers_t *handlers;
69
70
0
  for (handlers = profiling_handlers; handlers; handlers = handlers->next)
71
0
    if (handlers->accepted_data_types & data_type_mask)
72
0
      return 1;
73
0
  return 0;
74
0
}
Unexecuted instantiation: ipc.c:profiling_enabled
Unexecuted instantiation: action.c:profiling_enabled
Unexecuted instantiation: net_tcp_proc.c:profiling_enabled
Unexecuted instantiation: net_udp.c:profiling_enabled
Unexecuted instantiation: receive.c:profiling_enabled
Unexecuted instantiation: async.c:profiling_enabled
Unexecuted instantiation: timer.c:profiling_enabled
Unexecuted instantiation: profiling.c:profiling_enabled
75
76
static inline void profiling_msg_start(struct sip_msg *msg, int route_type,
77
  const char *route_name, int depth)
78
0
{
79
0
  profiling_handlers_t *handlers;
80
0
  for (handlers = profiling_handlers; handlers; handlers = handlers->next)
81
0
    if ((handlers->accepted_data_types & PROFILING_DATA_TYPE_SCRIPT) &&
82
0
      handlers->on_start)
83
0
      handlers->on_start(PROFILING_DATA_TYPE_SCRIPT, route_name,
84
0
        route_type, depth, msg);
85
0
}
Unexecuted instantiation: ipc.c:profiling_msg_start
Unexecuted instantiation: action.c:profiling_msg_start
Unexecuted instantiation: net_tcp_proc.c:profiling_msg_start
Unexecuted instantiation: net_udp.c:profiling_msg_start
Unexecuted instantiation: receive.c:profiling_msg_start
Unexecuted instantiation: async.c:profiling_msg_start
Unexecuted instantiation: timer.c:profiling_msg_start
Unexecuted instantiation: profiling.c:profiling_msg_start
86
87
static inline void profiling_msg_end(struct sip_msg *msg, int route_type,
88
  const char *route_name, int depth, int status)
89
0
{
90
0
  profiling_handlers_t *handlers;
91
0
  for (handlers = profiling_handlers; handlers; handlers = handlers->next)
92
0
    if ((handlers->accepted_data_types & PROFILING_DATA_TYPE_SCRIPT) &&
93
0
      handlers->on_end)
94
0
      handlers->on_end(PROFILING_DATA_TYPE_SCRIPT, route_name,
95
0
        route_type, depth, status, msg);
96
0
}
Unexecuted instantiation: ipc.c:profiling_msg_end
Unexecuted instantiation: action.c:profiling_msg_end
Unexecuted instantiation: net_tcp_proc.c:profiling_msg_end
Unexecuted instantiation: net_udp.c:profiling_msg_end
Unexecuted instantiation: receive.c:profiling_msg_end
Unexecuted instantiation: async.c:profiling_msg_end
Unexecuted instantiation: timer.c:profiling_msg_end
Unexecuted instantiation: profiling.c:profiling_msg_end
97
98
static inline void profiling_route_enter(struct sip_msg *msg, int route_type,
99
  const char *route_name, const char *file, int line,
100
  int depth)
101
0
{
102
0
  profiling_handlers_t *handlers;
103
0
  for (handlers = profiling_handlers; handlers; handlers = handlers->next)
104
0
    if ((handlers->accepted_data_types & PROFILING_DATA_TYPE_SCRIPT) &&
105
0
      handlers->on_enter)
106
0
      handlers->on_enter(PROFILING_DATA_TYPE_SCRIPT, route_name,
107
0
        route_type, depth, file, line, msg);
108
0
}
Unexecuted instantiation: ipc.c:profiling_route_enter
Unexecuted instantiation: action.c:profiling_route_enter
Unexecuted instantiation: net_tcp_proc.c:profiling_route_enter
Unexecuted instantiation: net_udp.c:profiling_route_enter
Unexecuted instantiation: receive.c:profiling_route_enter
Unexecuted instantiation: async.c:profiling_route_enter
Unexecuted instantiation: timer.c:profiling_route_enter
Unexecuted instantiation: profiling.c:profiling_route_enter
109
110
static inline void profiling_route_exit(struct sip_msg *msg, int route_type,
111
  const char *route_name, const char *file, int line,
112
  int depth, int status)
113
0
{
114
0
  profiling_handlers_t *handlers;
115
0
  for (handlers = profiling_handlers; handlers; handlers = handlers->next)
116
0
    if ((handlers->accepted_data_types & PROFILING_DATA_TYPE_SCRIPT) &&
117
0
      handlers->on_exit)
118
0
      handlers->on_exit(PROFILING_DATA_TYPE_SCRIPT, route_name,
119
0
        route_type, depth, file, line, status, msg);
120
0
}
Unexecuted instantiation: ipc.c:profiling_route_exit
Unexecuted instantiation: action.c:profiling_route_exit
Unexecuted instantiation: net_tcp_proc.c:profiling_route_exit
Unexecuted instantiation: net_udp.c:profiling_route_exit
Unexecuted instantiation: receive.c:profiling_route_exit
Unexecuted instantiation: async.c:profiling_route_exit
Unexecuted instantiation: timer.c:profiling_route_exit
Unexecuted instantiation: profiling.c:profiling_route_exit
121
122
static inline int profiling_get_ctx(profiling_ctx_t *ctx)
123
0
{
124
0
  profiling_handlers_t *handlers;
125
0
126
0
  for (handlers = profiling_handlers; handlers; handlers = handlers->next)
127
0
    if ((handlers->accepted_data_types & PROFILING_DATA_TYPE_SCRIPT) &&
128
0
      handlers->get_ctx && handlers->get_ctx(ctx))
129
0
      return 1;
130
0
131
0
  return 0;
132
0
}
Unexecuted instantiation: ipc.c:profiling_get_ctx
Unexecuted instantiation: action.c:profiling_get_ctx
Unexecuted instantiation: net_tcp_proc.c:profiling_get_ctx
Unexecuted instantiation: net_udp.c:profiling_get_ctx
Unexecuted instantiation: receive.c:profiling_get_ctx
Unexecuted instantiation: async.c:profiling_get_ctx
Unexecuted instantiation: timer.c:profiling_get_ctx
Unexecuted instantiation: profiling.c:profiling_get_ctx
133
134
static inline int profiling_set_ctx(const profiling_ctx_t *ctx)
135
0
{
136
0
  profiling_handlers_t *handlers;
137
0
  int ret = 0;
138
0
139
0
  for (handlers = profiling_handlers; handlers; handlers = handlers->next)
140
0
    if ((handlers->accepted_data_types & PROFILING_DATA_TYPE_SCRIPT) &&
141
0
      handlers->set_ctx && handlers->set_ctx(ctx))
142
0
      ret = 1;
143
0
144
0
  return ret;
145
0
}
Unexecuted instantiation: ipc.c:profiling_set_ctx
Unexecuted instantiation: action.c:profiling_set_ctx
Unexecuted instantiation: net_tcp_proc.c:profiling_set_ctx
Unexecuted instantiation: net_udp.c:profiling_set_ctx
Unexecuted instantiation: receive.c:profiling_set_ctx
Unexecuted instantiation: async.c:profiling_set_ctx
Unexecuted instantiation: timer.c:profiling_set_ctx
Unexecuted instantiation: profiling.c:profiling_set_ctx
146
147
148
149
/**************** structs and functions for process profiling **************/
150
151
static inline char * ss_merge256( char *s1, char *s2)
152
0
{
153
0
  static char b[256], *c;
154
0
  c = stpncpy( b, s1, 255);
155
0
  strncpy( c, s2, 255-(c-b));
156
0
  b[255] = 0;
157
0
  return b;
158
0
}
Unexecuted instantiation: ipc.c:ss_merge256
Unexecuted instantiation: action.c:ss_merge256
Unexecuted instantiation: net_tcp_proc.c:ss_merge256
Unexecuted instantiation: net_udp.c:ss_merge256
Unexecuted instantiation: receive.c:ss_merge256
Unexecuted instantiation: async.c:ss_merge256
Unexecuted instantiation: timer.c:ss_merge256
Unexecuted instantiation: profiling.c:ss_merge256
159
static inline char * sss_merge256( char *s1, char *s2, char *s3)
160
0
{
161
0
  static char b[256], *c;
162
0
  c = stpncpy( b, s1, 255);
163
0
  c = stpncpy( c, s2, 255-(c-b));
164
0
  strncpy( c, s3, 255-(c-b));
165
0
  b[255] = 0;
166
0
  return b;
167
0
}
Unexecuted instantiation: ipc.c:sss_merge256
Unexecuted instantiation: action.c:sss_merge256
Unexecuted instantiation: net_tcp_proc.c:sss_merge256
Unexecuted instantiation: net_udp.c:sss_merge256
Unexecuted instantiation: receive.c:sss_merge256
Unexecuted instantiation: async.c:sss_merge256
Unexecuted instantiation: timer.c:sss_merge256
Unexecuted instantiation: profiling.c:sss_merge256
168
169
170
/* Keep enough room for deeply nested proc enter() flows. */
171
0
#define PROFILING_PROC_STACK_MAX 16
172
typedef struct profiling_proc_pending {
173
  const char *name;
174
  const char *file;
175
  int line;
176
  uint16_t depth;
177
  uint8_t is_start;
178
} profiling_proc_pending_t;
179
180
/* stack for pending start+enter, waiting to be flushed */
181
extern profiling_proc_pending_t
182
  profiling_proc_pending_stack[PROFILING_PROC_STACK_MAX];
183
extern uint16_t profiling_proc_pending_no;
184
185
/* depth, as number of "enter"s done by process profiling */
186
extern uint16_t proc_depth;
187
188
189
#define profiling_proc_level_enabled(_level) \
190
0
  ( (_level!=LEVEL_OFF) && (_level<=pt[process_no].profiling_proc_level) )
191
192
static inline void profiling_proc_flush_pending(void)
193
0
{
194
0
  uint16_t i;
195
0
  profiling_handlers_t *handlers;
196
0
  profiling_proc_pending_t *rec;
197
198
0
  if (!profiling_proc_pending_no)
199
0
    return;
200
201
0
  for (i = 0; i < profiling_proc_pending_no; i++) {
202
0
    rec = &profiling_proc_pending_stack[i];
203
204
0
    for (handlers = profiling_handlers; handlers;
205
0
      handlers = handlers->next) {
206
0
      if (!(handlers->accepted_data_types &
207
0
        PROFILING_DATA_TYPE_PROC))
208
0
        continue;
209
210
0
      if (rec->is_start) {
211
0
        if (handlers->on_start)
212
0
          handlers->on_start(PROFILING_DATA_TYPE_PROC,
213
0
            rec->name, pt[process_no].type, i, NULL);
214
0
      } else {
215
0
        if (handlers->on_enter)
216
0
          handlers->on_enter(PROFILING_DATA_TYPE_PROC,
217
0
            rec->name, pt[process_no].type,
218
0
            i, rec->file, rec->line, NULL);
219
0
      }
220
0
    }
221
0
  }
222
223
0
  profiling_proc_pending_no = 0;
224
0
}
Unexecuted instantiation: ipc.c:profiling_proc_flush_pending
Unexecuted instantiation: action.c:profiling_proc_flush_pending
Unexecuted instantiation: net_tcp_proc.c:profiling_proc_flush_pending
Unexecuted instantiation: net_udp.c:profiling_proc_flush_pending
Unexecuted instantiation: receive.c:profiling_proc_flush_pending
Unexecuted instantiation: async.c:profiling_proc_flush_pending
Unexecuted instantiation: timer.c:profiling_proc_flush_pending
Unexecuted instantiation: profiling.c:profiling_proc_flush_pending
225
226
227
static inline void profiling_proc_start(enum profiling_proc_level level,
228
  int with_next)
229
0
{
230
0
  profiling_handlers_t *handlers;
231
232
0
  if (!profiling_proc_level_enabled(level))
233
0
    return;
234
235
0
  proc_depth = 0;
236
0
  if (with_next) {
237
0
    profiling_proc_pending_stack[proc_depth].name =
238
0
      pt[process_no].desc;
239
0
    profiling_proc_pending_stack[proc_depth].file = NULL;
240
0
    profiling_proc_pending_stack[proc_depth].line = 0;
241
0
    profiling_proc_pending_stack[proc_depth].is_start = 1;
242
0
    profiling_proc_pending_no = 1;
243
0
    return;
244
0
  }
245
246
0
  for (handlers = profiling_handlers; handlers; handlers = handlers->next)
247
0
    if ((handlers->accepted_data_types & PROFILING_DATA_TYPE_PROC) &&
248
0
      handlers->on_start)
249
0
      handlers->on_start(PROFILING_DATA_TYPE_PROC, pt[process_no].desc,
250
0
        pt[process_no].type, proc_depth, NULL);
251
0
}
Unexecuted instantiation: ipc.c:profiling_proc_start
Unexecuted instantiation: action.c:profiling_proc_start
Unexecuted instantiation: net_tcp_proc.c:profiling_proc_start
Unexecuted instantiation: net_udp.c:profiling_proc_start
Unexecuted instantiation: receive.c:profiling_proc_start
Unexecuted instantiation: async.c:profiling_proc_start
Unexecuted instantiation: timer.c:profiling_proc_start
Unexecuted instantiation: profiling.c:profiling_proc_start
252
253
254
static inline void profiling_proc_end(enum profiling_proc_level level,
255
  int status)
256
0
{
257
0
  profiling_handlers_t *handlers;
258
259
0
  if (!profiling_proc_level_enabled(level))
260
0
    return;
261
262
0
  if (profiling_proc_pending_no) {
263
0
    profiling_proc_pending_no--;
264
0
    return;
265
0
  }
266
267
0
  for (handlers = profiling_handlers; handlers; handlers = handlers->next)
268
0
    if ((handlers->accepted_data_types & PROFILING_DATA_TYPE_PROC) &&
269
0
      handlers->on_end)
270
0
      handlers->on_end(PROFILING_DATA_TYPE_PROC, pt[process_no].desc,
271
0
        pt[process_no].type, proc_depth, status, NULL);
272
0
}
Unexecuted instantiation: ipc.c:profiling_proc_end
Unexecuted instantiation: action.c:profiling_proc_end
Unexecuted instantiation: net_tcp_proc.c:profiling_proc_end
Unexecuted instantiation: net_udp.c:profiling_proc_end
Unexecuted instantiation: receive.c:profiling_proc_end
Unexecuted instantiation: async.c:profiling_proc_end
Unexecuted instantiation: timer.c:profiling_proc_end
Unexecuted instantiation: profiling.c:profiling_proc_end
273
274
275
#define profiling_proc_enter( _level, _name, _with_next ) \
276
0
  _profiling_proc_enter( _level, _name, __func__, __LINE__, _with_next)
277
static inline void _profiling_proc_enter(enum profiling_proc_level level,
278
  const char *name, const char *file, int line, int with_next)
279
0
{
280
0
  profiling_handlers_t *handlers;
281
282
0
  if (!profiling_proc_level_enabled(level))
283
0
    return;
284
285
0
  proc_depth++;
286
287
0
  if (with_next) {
288
0
    if (proc_depth < PROFILING_PROC_STACK_MAX) {
289
0
      profiling_proc_pending_stack[profiling_proc_pending_no].name =
290
0
        name;
291
0
      profiling_proc_pending_stack[profiling_proc_pending_no].file =
292
0
        file;
293
0
      profiling_proc_pending_stack[profiling_proc_pending_no].line =
294
0
        line;
295
0
      profiling_proc_pending_stack[profiling_proc_pending_no].is_start =
296
0
        0;
297
0
      profiling_proc_pending_no++;
298
0
    }
299
0
    return;
300
0
  }
301
302
0
  profiling_proc_flush_pending();
303
304
0
  for (handlers = profiling_handlers; handlers; handlers = handlers->next)
305
0
    if ((handlers->accepted_data_types & PROFILING_DATA_TYPE_PROC) &&
306
0
      handlers->on_enter)
307
0
      handlers->on_enter(PROFILING_DATA_TYPE_PROC, name,
308
0
        pt[process_no].type, proc_depth, file, line, NULL);
309
0
}
Unexecuted instantiation: ipc.c:_profiling_proc_enter
Unexecuted instantiation: action.c:_profiling_proc_enter
Unexecuted instantiation: net_tcp_proc.c:_profiling_proc_enter
Unexecuted instantiation: net_udp.c:_profiling_proc_enter
Unexecuted instantiation: receive.c:_profiling_proc_enter
Unexecuted instantiation: async.c:_profiling_proc_enter
Unexecuted instantiation: timer.c:_profiling_proc_enter
Unexecuted instantiation: profiling.c:_profiling_proc_enter
310
311
312
#define profiling_proc_exit( _level, _name, _status ) \
313
0
  _profiling_proc_exit( _level, _name, __func__, __LINE__, _status)
314
static inline void _profiling_proc_exit(enum profiling_proc_level level,
315
  const char *name, const char *file, int line, int status)
316
0
{
317
0
  profiling_handlers_t *handlers;
318
319
0
  if (!profiling_proc_level_enabled(level))
320
0
    return;
321
322
0
  proc_depth--;
323
324
0
  if (profiling_proc_pending_no) {
325
0
    profiling_proc_pending_no--;
326
0
    return;
327
0
  }
328
329
0
  for (handlers = profiling_handlers; handlers; handlers = handlers->next)
330
0
    if ((handlers->accepted_data_types & PROFILING_DATA_TYPE_PROC) &&
331
0
      handlers->on_exit)
332
0
      handlers->on_exit(PROFILING_DATA_TYPE_PROC, name,
333
        pt[process_no].type, proc_depth, file, line, status, NULL);
334
0
}
Unexecuted instantiation: ipc.c:_profiling_proc_exit
Unexecuted instantiation: action.c:_profiling_proc_exit
Unexecuted instantiation: net_tcp_proc.c:_profiling_proc_exit
Unexecuted instantiation: net_udp.c:_profiling_proc_exit
Unexecuted instantiation: receive.c:_profiling_proc_exit
Unexecuted instantiation: async.c:_profiling_proc_exit
Unexecuted instantiation: timer.c:_profiling_proc_exit
Unexecuted instantiation: profiling.c:_profiling_proc_exit
335
336
337
338
#endif /* PROFILING_H */