• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

tarantool / luajit / 13699178833

06 Mar 2025 12:36PM UTC coverage: 93.007% (+0.04%) from 92.968%
13699178833

push

github

Buristan
memprof: set default path to profiling output file

sysprof has an optional parameter `path`, that sets a path to
the profiling output file. By default, the path is `sysprof.bin`.
`misc.memprof.start()` requires setting a path to the profiling
output file. The patch fixes this inconsistency by introducing a
default path to the memprof profiling output file - `memprof.bin`.

Reviewed-by: Sergey Kaplun <skaplun@tarantool.org>
Signed-off-by: Sergey Kaplun <skaplun@tarantool.org>

5702 of 6038 branches covered (94.44%)

Branch coverage included in aggregate %.

2 of 2 new or added lines in 1 file covered. (100.0%)

45 existing lines in 5 files now uncovered.

21734 of 23461 relevant lines covered (92.64%)

2958736.69 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

88.93
/src/lib_misc.c
1
/*
2
** Miscellaneous Lua extensions library.
3
**
4
** Major portions taken verbatim or adapted from the LuaVela interpreter.
5
** Copyright (C) 2015-2019 IPONWEB Ltd.
6
*/
7

8
#define lib_misc_c
9
#define LUA_LIB
10

11
#include "lua.h"
12
#include "lmisclib.h"
13
#include "lauxlib.h"
14

15
#include "lj_obj.h"
16
#include "lj_str.h"
17
#include "lj_strfmt.h"
18
#include "lj_tab.h"
19
#include "lj_lib.h"
20
#include "lj_gc.h"
21
#include "lj_err.h"
22

23
#include "lj_memprof.h"
24

25
#include <errno.h>
26
#include <fcntl.h>
27
#if !LJ_TARGET_WINDOWS
28
#include <unistd.h>
29
#endif
30

31
/* ------------------------------------------------------------------------ */
32

33
static LJ_AINLINE void setnumfield(struct lua_State *L, GCtab *t,
34,868✔
34
                                   const char *name, int64_t val)
35
{
36
  setnumV(lj_tab_setstr(L, t, lj_str_newz(L, name)), (double)val);
34,868✔
37
}
38

39
#define LJLIB_MODULE_misc
40

41
LJLIB_CF(misc_getmetrics)
1,834✔
42
{
43
  struct luam_Metrics metrics;
1,834✔
44
  GCtab *m;
1,834✔
45

46
  lua_createtable(L, 0, 19);
1,834✔
47
  m = tabV(L->top - 1);
1,834✔
48

49
  luaM_metrics(L, &metrics);
1,834✔
50

51
  setnumfield(L, m, "strhash_hit", metrics.strhash_hit);
1,834✔
52
  setnumfield(L, m, "strhash_miss", metrics.strhash_miss);
1,834✔
53

54
  setnumfield(L, m, "gc_strnum", metrics.gc_strnum);
1,834✔
55
  setnumfield(L, m, "gc_tabnum", metrics.gc_tabnum);
1,834✔
56
  setnumfield(L, m, "gc_udatanum", metrics.gc_udatanum);
1,834✔
57
  setnumfield(L, m, "gc_cdatanum", metrics.gc_cdatanum);
1,834✔
58

59
  setnumfield(L, m, "gc_total", metrics.gc_total);
1,834✔
60
  setnumfield(L, m, "gc_freed", metrics.gc_freed);
1,834✔
61
  setnumfield(L, m, "gc_allocated", metrics.gc_allocated);
1,834✔
62

63
  setnumfield(L, m, "gc_steps_pause", metrics.gc_steps_pause);
1,834✔
64
  setnumfield(L, m, "gc_steps_propagate", metrics.gc_steps_propagate);
1,834✔
65
  setnumfield(L, m, "gc_steps_atomic", metrics.gc_steps_atomic);
1,834✔
66
  setnumfield(L, m, "gc_steps_sweepstring", metrics.gc_steps_sweepstring);
1,834✔
67
  setnumfield(L, m, "gc_steps_sweep", metrics.gc_steps_sweep);
1,834✔
68
  setnumfield(L, m, "gc_steps_finalize", metrics.gc_steps_finalize);
1,834✔
69

70
  setnumfield(L, m, "jit_snap_restore", metrics.jit_snap_restore);
1,834✔
71
  setnumfield(L, m, "jit_trace_abort", metrics.jit_trace_abort);
1,834✔
72
  setnumfield(L, m, "jit_mcode_size", metrics.jit_mcode_size);
1,834✔
73
  setnumfield(L, m, "jit_trace_num", metrics.jit_trace_num);
1,834✔
74

75
  return 1;
1,834✔
76
}
77

78
/* ------------------------------------------------------------------------ */
79

80
#include "lj_libdef.h"
81

82
/* --------- profile common section --------------------------------------- */
83

84
#if !LJ_TARGET_WINDOWS
85
/*
86
** Yep, 8Mb. Tuned in order not to bother the platform with too often flushes.
87
*/
88
#define STREAM_BUFFER_SIZE (8 * 1024 * 1024)
89

90
/* Structure given as ctx to memprof writer and on_stop callback. */
91
struct profile_ctx {
92
  /* Output file descriptor for data. */
93
  int fd;
94
  /* Profiled global_State for lj_mem_free at on_stop callback. */
95
  global_State *g;
96
  /* Buffer for data. */
97
  uint8_t buf[STREAM_BUFFER_SIZE];
98
};
99

100
/*
101
** Default buffer writer function.
102
** Just call write to the corresponding descriptor.
103
*/
104
static size_t buffer_writer_default(const void **buf_addr, size_t len,
21✔
105
                                    void *opt)
106
{
107
  struct profile_ctx *ctx = opt;
21✔
108
  const int fd = ctx->fd;
21✔
109
  const void * const buf_start = *buf_addr;
21✔
110
  const void *data = *buf_addr;
21✔
111
  size_t write_total = 0;
21✔
112

113
  lj_assertX(len <= STREAM_BUFFER_SIZE, "stream buffer overflow");
21✔
114

115
  for (;;) {
21✔
116
    const ssize_t written = write(fd, data, len - write_total);
21✔
117

118
    if (LJ_UNLIKELY(written == -1)) {
21✔
119
      /* Re-tries write in case of EINTR. */
UNCOV
120
      if (errno != EINTR) {
×
121
  /* Will be freed as whole chunk later. */
122
  *buf_addr = NULL;
×
UNCOV
123
  return write_total;
×
124
      }
125

126
      errno = 0;
×
UNCOV
127
      continue;
×
128
    }
129

130
    write_total += written;
21✔
131
    lj_assertX(write_total <= len, "invalid stream buffer write");
21✔
132

133
    if (write_total == len)
21✔
134
      break;
135

UNCOV
136
    data = (uint8_t *)data + (ptrdiff_t)written;
×
137
  }
138

139
  *buf_addr = buf_start;
21✔
140
  return write_total;
21✔
141
}
142

143
/* Default on stop callback. Just close the corresponding descriptor. */
144
static int on_stop_cb_default(void *opt, uint8_t *buf)
27✔
145
{
146
  struct profile_ctx *ctx = NULL;
27✔
147
  int fd = 0;
27✔
148

149
  if (opt == NULL) {
27✔
150
    /* Nothing to do. */
151
    return 0;
152
  }
153

154
  ctx = opt;
22✔
155
  fd = ctx->fd;
22✔
156
  UNUSED(buf);
22✔
157
  lj_mem_free(ctx->g, ctx, sizeof(*ctx));
22✔
158
  return close(fd);
22✔
159
}
160

161
/* ----- misc.sysprof module ---------------------------------------------- */
162

163
#define LJLIB_MODULE_misc_sysprof
164

165
/* The default profiling interval equals to 10 ms. */
166
#define SYSPROF_DEFAULT_INTERVAL 10
167
#define SYSPROF_DEFAULT_MODE "D"
168
#define SYSPROF_DEFAULT_OUTPUT "sysprof.bin"
169

170
static int set_output_path(const char *path, struct luam_Sysprof_Options *opt) {
5✔
171
  struct profile_ctx *ctx = opt->ctx;
5✔
172
  int fd = 0;
5✔
173
  lj_assertX(path != NULL, "no file to open by sysprof");
5✔
174
  fd = open(path, O_CREAT | O_WRONLY | O_TRUNC, 0644);
10✔
175
  if(fd == -1) {
5✔
176
    return PROFILE_ERRIO;
177
  }
178
  ctx->fd = fd;
4✔
179
  return PROFILE_SUCCESS;
4✔
180
}
181

182
static int parse_sysprof_opts(lua_State *L, struct luam_Sysprof_Options *opt,
19✔
183
                              const char **err_details) {
184
  int n = (int)(L->top - L->base);
19✔
185
  GCtab *options;
19✔
186
  if (n == 0) {
19✔
UNCOV
187
    opt->mode = LUAM_SYSPROF_DEFAULT;
×
UNCOV
188
    opt->interval = SYSPROF_DEFAULT_INTERVAL;
×
UNCOV
189
    return PROFILE_SUCCESS;
×
190
  }
191

192
  /* All other arguments given to this function are ignored. */
193
  options = lj_lib_checktab(L, 1);
19✔
194

195
  /* Get profiling mode. */
196
  {
197
    const char *mode = NULL;
18✔
198

199
    cTValue *mode_opt = lj_tab_getstr(options, lj_str_newlit(L, "mode"));
18✔
200
    if (mode_opt) {
18✔
201
      if (!tvisstr(mode_opt)) {
17✔
UNCOV
202
        *err_details = err2msg(LJ_ERR_PROF_DETAILS_BADMODE);
×
UNCOV
203
        return PROFILE_ERRUSE;
×
204
      }
205
      mode = strVdata(mode_opt);
17✔
206
      if (strlen(mode) == 0 || mode[1] != '\0') {
17✔
UNCOV
207
        *err_details = err2msg(LJ_ERR_PROF_DETAILS_BADMODE);
×
UNCOV
208
        return PROFILE_ERRUSE;
×
209
      }
210
    }
211

212
    if (!mode)
213
      mode = SYSPROF_DEFAULT_MODE;
214

215
    switch (*mode) {
18✔
216
      case 'D':
7✔
217
        opt->mode = LUAM_SYSPROF_DEFAULT;
7✔
218
        break;
7✔
219
      case 'L':
1✔
220
        opt->mode = LUAM_SYSPROF_LEAF;
1✔
221
        break;
1✔
222
      case 'C':
8✔
223
        opt->mode = LUAM_SYSPROF_CALLGRAPH;
8✔
224
        break;
8✔
225
      default:
2✔
226
        *err_details = err2msg(LJ_ERR_PROF_DETAILS_BADMODE);
2✔
227
        return PROFILE_ERRUSE;
2✔
228
    }
229
  }
230

231
  /* Get profiling interval. */
232
  {
233
    cTValue *interval = lj_tab_getstr(options, lj_str_newlit(L, "interval"));
16✔
234
    opt->interval = SYSPROF_DEFAULT_INTERVAL;
16✔
235
    if (interval && tvisnumber(interval)) {
16✔
236
      int32_t signed_interval = numberVint(interval);
8✔
237
      if (signed_interval < 1) {
8✔
238
        *err_details = err2msg(LJ_ERR_PROF_DETAILS_BADINTERVAL);
3✔
239
        return PROFILE_ERRUSE;
3✔
240
      }
241
      opt->interval = signed_interval;
5✔
242
    }
243
  }
244

245
  /* Get output path. */
246
  if (opt->mode != LUAM_SYSPROF_DEFAULT)
13✔
247
  {
248
    const char *path = NULL;
6✔
249
    struct profile_ctx *ctx = NULL;
6✔
250
    int status = 0;
6✔
251

252
    cTValue *pathtv = lj_tab_getstr(options, lj_str_newlit(L, "path"));
6✔
253
    if (!pathtv) {
6✔
254
      path = SYSPROF_DEFAULT_OUTPUT;
255
    } else if (!tvisstr(pathtv)) {
6✔
256
      *err_details = err2msg(LJ_ERR_PROF_DETAILS_BADPATH);
1✔
257
      return PROFILE_ERRUSE;
1✔
258
    } else {
259
      path = strVdata(pathtv);
5✔
260
    }
261

262
    ctx = lj_mem_new(L, sizeof(*ctx));
5✔
263
    ctx->g = G(L);
5✔
264
    opt->ctx = ctx;
5✔
265
    opt->buf = ctx->buf;
5✔
266
    opt->len = STREAM_BUFFER_SIZE;
5✔
267

268
    status = set_output_path(path, opt);
5✔
269
    if (status != PROFILE_SUCCESS) {
4✔
270
      *err_details = path;
1✔
271
      lj_mem_free(ctx->g, ctx, sizeof(*ctx));
1✔
272
      return status;
1✔
273
    }
274
  }
275

276
  return PROFILE_SUCCESS;
277
}
278

279
static int sysprof_error(lua_State *L, int status, const char *err_details)
9✔
280
{
281
  switch (status) {
9✔
282
    case PROFILE_ERRUSE:
6✔
283
      lua_pushnil(L);
6✔
284
      if (err_details)
6✔
285
        lj_strfmt_pushf(L, "%s: %s", err2msg(LJ_ERR_PROF_MISUSE), err_details);
6✔
286
      else
UNCOV
287
        lua_pushstring(L, err2msg(LJ_ERR_PROF_MISUSE));
×
288
      lua_pushinteger(L, EINVAL);
6✔
289
      return 3;
6✔
290
#if LJ_HASSYSPROF
291
    case PROFILE_ERRRUN:
2✔
292
      lua_pushnil(L);
2✔
293
      lua_pushstring(L, err2msg(LJ_ERR_PROF_ISRUNNING));
2✔
294
      lua_pushinteger(L, EINVAL);
2✔
295
      return 3;
2✔
296
    case PROFILE_ERRIO:
1✔
297
      return luaL_fileresult(L, 0, err_details);
1✔
298
#endif
299
    default:
300
      lj_assertL(0, "bad sysprof error %d", status);
301
      return 0;
302
  }
303
}
304

305
/* local res, err, errno = sysprof.start(options) */
306
LJLIB_CF(misc_sysprof_start)
19✔
307
{
308
#if !LJ_HASSYSPROF
309
  const char *err_details = err2msg(LJ_ERR_PROF_DETAILS_DISABLED);
310
  return sysprof_error(L, PROFILE_ERRUSE, err_details);
311
#else
312
  int status = PROFILE_SUCCESS;
19✔
313

314
  struct luam_Sysprof_Options opt = {};
19✔
315
  const char *err_details = NULL;
19✔
316

317
  status = parse_sysprof_opts(L, &opt, &err_details);
19✔
318
  if (LJ_UNLIKELY(status != PROFILE_SUCCESS))
18✔
319
    return sysprof_error(L, status, err_details);
7✔
320

321
  status = luaM_sysprof_start(L, &opt);
11✔
322
  if (LJ_UNLIKELY(status != PROFILE_SUCCESS))
11✔
323
    /* Allocated memory will be freed in on_stop callback. */
324
    return sysprof_error(L, status, err_details);
1✔
325

326
  lua_pushboolean(L, 1);
10✔
327
  return 1;
10✔
328
#endif /* !LJ_HASSYSPROF */
329
}
330

331
/* local res, err, errno = profile.sysprof_stop() */
332
LJLIB_CF(misc_sysprof_stop)
11✔
333
{
334
#if !LJ_HASSYSPROF
335
  const char *err_details = err2msg(LJ_ERR_PROF_DETAILS_DISABLED);
336
  return sysprof_error(L, PROFILE_ERRUSE, err_details);
337
#else
338
  int status = luaM_sysprof_stop(L);
11✔
339
  if (LJ_UNLIKELY(status != PROFILE_SUCCESS))
11✔
340
    return sysprof_error(L, status, NULL);
1✔
341

342
  lua_pushboolean(L, 1);
10✔
343
  return 1;
10✔
344
#endif /* !LJ_HASSYSPROF */
345
}
346

347
/* local counters, err, errno = sysprof.report() */
348
LJLIB_CF(misc_sysprof_report)
2✔
349
{
350
#if !LJ_HASSYSPROF
351
  const char *err_details = err2msg(LJ_ERR_PROF_DETAILS_DISABLED);
352
  return sysprof_error(L, PROFILE_ERRUSE, err_details);
353
#else
354
  struct luam_Sysprof_Counters counters = {};
2✔
355
  GCtab *data_tab = NULL;
2✔
356
  GCtab *count_tab = NULL;
2✔
357
  int status = luaM_sysprof_report(&counters);
2✔
358
  if (status != PROFILE_SUCCESS)
2✔
UNCOV
359
    return sysprof_error(L, status, NULL);
×
360

361
  lua_createtable(L, 0, 3);
2✔
362
  data_tab = tabV(L->top - 1);
2✔
363

364
  setnumfield(L, data_tab, "samples", counters.samples);
2✔
365

366
  lua_createtable(L, 0, LJ_VMST__MAX + 1);
2✔
367
  count_tab = tabV(L->top - 1);
2✔
368

369
  setnumfield(L, count_tab, "INTERP", counters.vmst_interp);
2✔
370
  setnumfield(L, count_tab, "LFUNC",  counters.vmst_lfunc);
2✔
371
  setnumfield(L, count_tab, "FFUNC",  counters.vmst_ffunc);
2✔
372
  setnumfield(L, count_tab, "CFUNC",  counters.vmst_cfunc);
2✔
373
  setnumfield(L, count_tab, "GC",     counters.vmst_gc);
2✔
374
  setnumfield(L, count_tab, "EXIT",   counters.vmst_exit);
2✔
375
  setnumfield(L, count_tab, "RECORD", counters.vmst_record);
2✔
376
  setnumfield(L, count_tab, "OPT",    counters.vmst_opt);
2✔
377
  setnumfield(L, count_tab, "ASM",    counters.vmst_asm);
2✔
378
  setnumfield(L, count_tab, "TRACE",  counters.vmst_trace);
2✔
379

380
  lua_setfield(L, -2, "vmstate");
2✔
381

382
  return 1;
2✔
383
#endif /* !LJ_HASSYSPROF */
384
}
385

386
/* ----- misc.memprof module ---------------------------------------------- */
387

388
#define LJLIB_MODULE_misc_memprof
389

390
#define MEMPROF_DEFAULT_OUTPUT "memprof.bin"
391

392
/* local started, err, errno = misc.memprof.start(fname) */
393
LJLIB_CF(misc_memprof_start)
19✔
394
{
395
#if !LJ_HASMEMPROF
396
  const char *err_details = err2msg(LJ_ERR_PROF_DETAILS_DISABLED);
397
  return sysprof_error(L, PROFILE_ERRUSE, err_details);
398
#else
399
  struct lj_memprof_options opt = {0};
19✔
400
  GCstr *s = lj_lib_optstr(L, 1);
19✔
401
  const char *fname = s ? strdata(s) : MEMPROF_DEFAULT_OUTPUT;
19✔
402
  struct profile_ctx *ctx;
19✔
403
  int memprof_status;
19✔
404

405
  /*
406
  ** FIXME: more elegant solution with ctx.
407
  ** Throws in case of OOM.
408
  */
409
  ctx = lj_mem_new(L, sizeof(*ctx));
19✔
410
  opt.ctx = ctx;
19✔
411
  opt.buf = ctx->buf;
19✔
412
  opt.writer = buffer_writer_default;
19✔
413
  opt.on_stop = on_stop_cb_default;
19✔
414
  opt.len = STREAM_BUFFER_SIZE;
19✔
415

416
  ctx->g = G(L);
19✔
417
  ctx->fd = open(fname, O_CREAT | O_WRONLY | O_TRUNC, 0644);
19✔
418

419
  if (ctx->fd == -1) {
19✔
420
    lj_mem_free(ctx->g, ctx, sizeof(*ctx));
1✔
421
    return luaL_fileresult(L, 0, fname);
1✔
422
  }
423

424
  memprof_status = lj_memprof_start(L, &opt);
18✔
425

426
  if (LJ_UNLIKELY(memprof_status != PROFILE_SUCCESS)) {
18✔
427
    switch (memprof_status) {
1✔
UNCOV
428
    case PROFILE_ERRUSE:
×
UNCOV
429
      lua_pushnil(L);
×
UNCOV
430
      lua_pushstring(L, err2msg(LJ_ERR_PROF_MISUSE));
×
UNCOV
431
      lua_pushinteger(L, EINVAL);
×
432
      return 3;
×
433
#if LJ_HASMEMPROF
434
    case PROFILE_ERRRUN:
1✔
435
      lua_pushnil(L);
1✔
436
      lua_pushstring(L, err2msg(LJ_ERR_PROF_ISRUNNING));
1✔
437
      lua_pushinteger(L, EINVAL);
1✔
438
      return 3;
1✔
UNCOV
439
    case PROFILE_ERRIO:
×
UNCOV
440
      return luaL_fileresult(L, 0, fname);
×
441
#endif
442
    default:
443
      lj_assertL(0, "bad memprof error %d", memprof_status);
444
      return 0;
445
    }
446
  }
447
  lua_pushboolean(L, 1);
17✔
448
  return 1;
17✔
449
#endif /* !LJ_HASMEMPROF */
450
}
451

452
/* local stopped, err, errno = misc.memprof.stop() */
453
LJLIB_CF(misc_memprof_stop)
18✔
454
{
455
#if !LJ_HASMEMPROF
456
  const char *err_details = err2msg(LJ_ERR_PROF_DETAILS_DISABLED);
457
  return sysprof_error(L, PROFILE_ERRUSE, err_details);
458
#else
459
  int status = lj_memprof_stop(L);
18✔
460
  if (status != PROFILE_SUCCESS) {
18✔
461
    switch (status) {
1✔
UNCOV
462
    case PROFILE_ERRUSE:
×
UNCOV
463
      lua_pushnil(L);
×
UNCOV
464
      lua_pushstring(L, err2msg(LJ_ERR_PROF_MISUSE));
×
UNCOV
465
      lua_pushinteger(L, EINVAL);
×
UNCOV
466
      return 3;
×
467
#if LJ_HASMEMPROF
468
    case PROFILE_ERRRUN:
1✔
469
      lua_pushnil(L);
1✔
470
      lua_pushstring(L, err2msg(LJ_ERR_PROF_NOTRUNNING));
1✔
471
      lua_pushinteger(L, EINVAL);
1✔
472
      return 3;
1✔
UNCOV
473
    case PROFILE_ERRIO:
×
UNCOV
474
      return luaL_fileresult(L, 0, NULL);
×
475
#endif
476
    default:
477
      lj_assertL(0, "bad memprof error %d", status);
478
      return 0;
479
    }
480
  }
481
  lua_pushboolean(L, 1);
17✔
482
  return 1;
17✔
483
#endif /* !LJ_HASMEMPROF */
484
}
485
#endif /* !LJ_TARGET_WINDOWS */
486

487
#include "lj_libdef.h"
488

489
/* ------------------------------------------------------------------------ */
490

491
LUALIB_API int luaopen_misc(struct lua_State *L)
374✔
492
{
493
#if !LJ_TARGET_WINDOWS
494
  luaM_sysprof_set_writer(buffer_writer_default);
374✔
495
  luaM_sysprof_set_on_stop(on_stop_cb_default);
374✔
496
  /*
497
  ** XXX: Passing NULL to the backtracer configuration handle sets the default
498
  ** backtracing function.
499
  */
500
  luaM_sysprof_set_backtracer(NULL);
374✔
501
#endif /* !LJ_TARGET_WINDOWS */
502

503
  LJ_LIB_REG(L, LUAM_MISCLIBNAME, misc);
374✔
504
#if !LJ_TARGET_WINDOWS
505
  LJ_LIB_REG(L, LUAM_MISCLIBNAME ".memprof", misc_memprof);
374✔
506
  LJ_LIB_REG(L, LUAM_MISCLIBNAME ".sysprof", misc_sysprof);
374✔
507
#endif /* !LJ_TARGET_WINDOWS */
508
  return 1;
374✔
509
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc