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

tarantool / luajit / 13392800825

18 Feb 2025 02:20PM UTC coverage: 92.965% (+0.03%) from 92.934%
13392800825

push

github

ligurio
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 to set a path to profiling output
file. The patch fixes this inconsistency by introducing a default
path to the memprof profiling output file - `memprof.bin`.

5704 of 6040 branches covered (94.44%)

Branch coverage included in aggregate %.

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

24 existing lines in 5 files now uncovered.

21729 of 23469 relevant lines covered (92.59%)

2957147.99 hits per line

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

87.9
/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_tab.h"
18
#include "lj_lib.h"
19
#include "lj_gc.h"
20
#include "lj_err.h"
21

22
#include "lj_memprof.h"
23

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

30
/* ------------------------------------------------------------------------ */
31

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

38
#define LJLIB_MODULE_misc
39

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

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

48
  luaM_metrics(L, &metrics);
1,524✔
49

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

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

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

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

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

74
  return 1;
1,524✔
75
}
76

77
/* ------------------------------------------------------------------------ */
78

79
#include "lj_libdef.h"
80

81
/* --------- profile common section --------------------------------------- */
82

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

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

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

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

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

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

125
      errno = 0;
×
126
      continue;
×
127
    }
128

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

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

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

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

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

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

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

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

162
#define LJLIB_MODULE_misc_sysprof
163

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

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

181
static int parse_sysprof_opts(lua_State *L, struct luam_Sysprof_Options *opt,
16✔
182
                                  const char **err_details) {
183
  int n = (int)(L->top - L->base);
16✔
184
  if (n != 1) {
16✔
185
    opt->mode = LUAM_SYSPROF_DEFAULT;
×
186
    opt->interval = SYSPROF_DEFAULT_INTERVAL;
×
187
    goto set_path;
×
188
  }
189

190
  if (!lua_istable(L, 1)) {
16✔
191
    *err_details = err2msg(LJ_ERR_PROF_DETAILS_BADTABLE);
1✔
192
    return PROFILE_ERRUSE;
1✔
193
  }
194

195
  GCtab *options = lj_lib_checktab(L, 1);
15✔
196

197
  /* Get profiling mode. */
198
  {
199
    const char *mode = NULL;
15✔
200

201
    cTValue *mode_opt = lj_tab_getstr(options, lj_str_newlit(L, "mode"));
15✔
202
    if (mode_opt) {
15✔
203
      if (!tvisstr(mode_opt)) {
14✔
204
        *err_details = err2msg(LJ_ERR_PROF_DETAILS_BADMODE);
×
205
        return PROFILE_ERRUSE;
×
206
      }
207
      mode = strVdata(mode_opt);
14✔
208
      if (strlen(mode) > 0 && mode[1] != '\0') {
14✔
209
        *err_details = err2msg(LJ_ERR_PROF_DETAILS_BADMODE);
×
210
        return PROFILE_ERRUSE;
×
211
      }
212
    }
213

214
    if (!mode)
215
      mode = SYSPROF_DEFAULT_MODE;
216

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

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

247
set_path:
7✔
248

249
  /* Get output path. */
250
  if (opt->mode != LUAM_SYSPROF_DEFAULT)
12✔
251
  {
252
    const char *path = NULL;
5✔
253
    struct profile_ctx *ctx = NULL;
5✔
254
    int status = 0;
5✔
255

256
    cTValue *pathtv = lj_tab_getstr(options, lj_str_newlit(L, "path"));
5✔
257
    if (!pathtv)
5✔
258
      path = SYSPROF_DEFAULT_OUTPUT;
259
    else if (!tvisstr(pathtv)) {
5✔
260
      *err_details = err2msg(LJ_ERR_PROF_DETAILS_BADPATH);
×
261
      return PROFILE_ERRUSE;
×
262
    }
263
    else
264
      path = strVdata(pathtv);
5✔
265

266
    ctx = lj_mem_new(L, sizeof(*ctx));
5✔
267
    ctx->g = G(L);
5✔
268
    opt->ctx = ctx;
5✔
269
    opt->buf = ctx->buf;
5✔
270
    opt->len = STREAM_BUFFER_SIZE;
5✔
271

272
    status = set_output_path(path, opt);
5✔
273
    if (status != PROFILE_SUCCESS) {
4✔
274
      lj_mem_free(ctx->g, ctx, sizeof(*ctx));
1✔
275
      return status;
1✔
276
    }
277
  }
278

279
  return PROFILE_SUCCESS;
280
}
281

282
static int sysprof_error(lua_State *L, int status, const char *err_details)
7✔
283
{
284
  switch (status) {
7✔
285
    case PROFILE_ERRUSE:
4✔
286
      lua_pushnil(L);
4✔
287
      lua_pushstring(L, err2msg(LJ_ERR_PROF_MISUSE));
4✔
288
      if (err_details) {
4✔
289
        lua_pushstring(L, ": ");
4✔
290
        lua_pushstring(L, err_details);
4✔
291
        lua_concat(L, 3);
4✔
292
      }
293
      lua_pushinteger(L, EINVAL);
4✔
294
      return 3;
4✔
295
#if LJ_HASSYSPROF
296
    case PROFILE_ERRRUN:
2✔
297
      lua_pushnil(L);
2✔
298
      lua_pushstring(L, err2msg(LJ_ERR_PROF_ISRUNNING));
2✔
299
      if (err_details) {
2✔
300
        lua_pushstring(L, ": ");
×
301
        lua_pushstring(L, err_details);
×
302
        lua_concat(L, 3);
×
303
      }
304
      lua_pushinteger(L, EINVAL);
2✔
305
      return 3;
2✔
306
    case PROFILE_ERRIO:
1✔
307
      return luaL_fileresult(L, 0, NULL);
1✔
308
#endif
309
    default:
310
      lj_assertL(0, "bad sysprof error %d", status);
311
      return 0;
312
  }
313
}
314

315
/* local res, err, errno = sysprof.start(options) */
316
LJLIB_CF(misc_sysprof_start)
16✔
317
{
318
  const char *err_details = NULL;
16✔
319
#if !LJ_HASSYSPROF
320
  err_details = err2msg(LJ_ERR_PROF_DETAILS_DISABLED);
321
  return sysprof_error(L, PROFILE_ERRUSE, err_details);
322
#endif /* !LJ_HASSYSPROF */
323

324
  int status = PROFILE_SUCCESS;
16✔
325

326
  struct luam_Sysprof_Options opt = {};
16✔
327

328
  status = parse_sysprof_opts(L, &opt, &err_details);
16✔
329
  if (LJ_UNLIKELY(status != PROFILE_SUCCESS))
16✔
330
    return sysprof_error(L, status, err_details);
5✔
331

332
  status = luaM_sysprof_start(L, &opt);
11✔
333
  if (LJ_UNLIKELY(status != PROFILE_SUCCESS))
11✔
334
    /* Allocated memory will be freed in on_stop callback. */
335
    return sysprof_error(L, status, err_details);
1✔
336

337
  lua_pushboolean(L, 1);
10✔
338
  return 1;
10✔
339
}
340

341
/* local res, err, errno = profile.sysprof_stop() */
342
LJLIB_CF(misc_sysprof_stop)
11✔
343
{
344
#if !LJ_HASSYSPROF
345
  const char *err_details = NULL;
346
  err_details = err2msg(LJ_ERR_PROF_DETAILS_DISABLED);
347
  return sysprof_error(L, PROFILE_ERRUSE, err_details);
348
#endif /* !LJ_HASSYSPROF */
349
  int status = luaM_sysprof_stop(L);
11✔
350
  if (LJ_UNLIKELY(status != PROFILE_SUCCESS))
11✔
351
    return sysprof_error(L, status, NULL);
1✔
352

353
  lua_pushboolean(L, 1);
10✔
354
  return 1;
10✔
355
}
356

357
/* local counters, err, errno = sysprof.report() */
358
LJLIB_CF(misc_sysprof_report)
2✔
359
{
360
#if !LJ_HASSYSPROF
361
  const char *err_details = NULL;
362
  err_details = err2msg(LJ_ERR_PROF_DETAILS_DISABLED);
363
  return sysprof_error(L, PROFILE_ERRUSE, err_details);
364
#endif /* !LJ_HASSYSPROF */
365
  struct luam_Sysprof_Counters counters = {};
2✔
366
  GCtab *data_tab = NULL;
2✔
367
  GCtab *count_tab = NULL;
2✔
368

369
  int status = luaM_sysprof_report(&counters);
2✔
370
  if (status != PROFILE_SUCCESS)
2✔
371
    return sysprof_error(L, status, NULL);
×
372

373
  lua_createtable(L, 0, 3);
2✔
374
  data_tab = tabV(L->top - 1);
2✔
375

376
  setnumfield(L, data_tab, "samples", counters.samples);
2✔
377

378
  lua_createtable(L, 0, LJ_VMST__MAX + 1);
2✔
379
  count_tab = tabV(L->top - 1);
2✔
380

381
  setnumfield(L, count_tab, "INTERP", counters.vmst_interp);
2✔
382
  setnumfield(L, count_tab, "LFUNC",  counters.vmst_lfunc);
2✔
383
  setnumfield(L, count_tab, "FFUNC",  counters.vmst_ffunc);
2✔
384
  setnumfield(L, count_tab, "CFUNC",  counters.vmst_cfunc);
2✔
385
  setnumfield(L, count_tab, "GC",     counters.vmst_gc);
2✔
386
  setnumfield(L, count_tab, "EXIT",   counters.vmst_exit);
2✔
387
  setnumfield(L, count_tab, "RECORD", counters.vmst_record);
2✔
388
  setnumfield(L, count_tab, "OPT",    counters.vmst_opt);
2✔
389
  setnumfield(L, count_tab, "ASM",    counters.vmst_asm);
2✔
390
  setnumfield(L, count_tab, "TRACE",  counters.vmst_trace);
2✔
391

392
  lua_setfield(L, -2, "vmstate");
2✔
393

394
  return 1;
2✔
395
}
396

397
/* ----- misc.memprof module ---------------------------------------------- */
398

399
#define LJLIB_MODULE_misc_memprof
400

401
#define MEMPROF_DEFAULT_OUTPUT "memprof.bin"
402

403
/* local started, err, errno = misc.memprof.start(fname) */
404
LJLIB_CF(misc_memprof_start)
19✔
405
{
406
#if !LJ_HASMEMPROF
407
  const char *err_details = NULL;
408
  err_details = err2msg(LJ_ERR_PROF_DETAILS_DISABLED);
409
  return sysprof_error(L, PROFILE_ERRUSE, err_details);
410
#endif /* !LJ_HASMEMPROF */
411
  struct lj_memprof_options opt = {0};
19✔
412
  GCstr *s = lj_lib_optstr(L, 1);
19✔
413
  const char *fname = s ? strdata(s) : MEMPROF_DEFAULT_OUTPUT;
19✔
414
  struct profile_ctx *ctx;
19✔
415
  int memprof_status;
19✔
416

417
  /*
418
  ** FIXME: more elegant solution with ctx.
419
  ** Throws in case of OOM.
420
  */
421
  ctx = lj_mem_new(L, sizeof(*ctx));
19✔
422
  opt.ctx = ctx;
19✔
423
  opt.buf = ctx->buf;
19✔
424
  opt.writer = buffer_writer_default;
19✔
425
  opt.on_stop = on_stop_cb_default;
19✔
426
  opt.len = STREAM_BUFFER_SIZE;
19✔
427

428
  ctx->g = G(L);
19✔
429
  ctx->fd = open(fname, O_CREAT | O_WRONLY | O_TRUNC, 0644);
19✔
430

431
  if (ctx->fd == -1) {
19✔
432
    lj_mem_free(ctx->g, ctx, sizeof(*ctx));
1✔
433
    return luaL_fileresult(L, 0, fname);
1✔
434
  }
435

436
  memprof_status = lj_memprof_start(L, &opt);
18✔
437

438
  if (LJ_UNLIKELY(memprof_status != PROFILE_SUCCESS)) {
18✔
439
    switch (memprof_status) {
1✔
UNCOV
440
    case PROFILE_ERRUSE:
×
UNCOV
441
      lua_pushnil(L);
×
UNCOV
442
      lua_pushstring(L, err2msg(LJ_ERR_PROF_MISUSE));
×
443
      lua_pushinteger(L, EINVAL);
×
444
      return 3;
×
445
#if LJ_HASMEMPROF
446
    case PROFILE_ERRRUN:
1✔
447
      lua_pushnil(L);
1✔
448
      lua_pushstring(L, err2msg(LJ_ERR_PROF_ISRUNNING));
1✔
449
      lua_pushinteger(L, EINVAL);
1✔
450
      return 3;
1✔
UNCOV
451
    case PROFILE_ERRIO:
×
UNCOV
452
      return luaL_fileresult(L, 0, fname);
×
453
#endif
454
    default:
455
      lj_assertL(0, "bad memprof error %d", memprof_status);
456
      return 0;
457
    }
458
  }
459
  lua_pushboolean(L, 1);
17✔
460
  return 1;
17✔
461
}
462

463
/* local stopped, err, errno = misc.memprof.stop() */
464
LJLIB_CF(misc_memprof_stop)
18✔
465
{
466
#if !LJ_HASMEMPROF
467
  const char *err_details = NULL;
468
  err_details = err2msg(LJ_ERR_PROF_DETAILS_DISABLED);
469
  return sysprof_error(L, PROFILE_ERRUSE, err_details);
470
#endif /* !LJ_HASMEMPROF */
471
  int status = lj_memprof_stop(L);
18✔
472
  if (status != PROFILE_SUCCESS) {
18✔
473
    switch (status) {
1✔
UNCOV
474
    case PROFILE_ERRUSE:
×
UNCOV
475
      lua_pushnil(L);
×
UNCOV
476
      lua_pushstring(L, err2msg(LJ_ERR_PROF_MISUSE));
×
477
      lua_pushinteger(L, EINVAL);
×
478
      return 3;
×
479
#if LJ_HASMEMPROF
480
    case PROFILE_ERRRUN:
1✔
481
      lua_pushnil(L);
1✔
482
      lua_pushstring(L, err2msg(LJ_ERR_PROF_NOTRUNNING));
1✔
483
      lua_pushinteger(L, EINVAL);
1✔
484
      return 3;
1✔
UNCOV
485
    case PROFILE_ERRIO:
×
UNCOV
486
      return luaL_fileresult(L, 0, NULL);
×
487
#endif
488
    default:
489
      lj_assertL(0, "bad memprof error %d", status);
490
      return 0;
491
    }
492
  }
493
  lua_pushboolean(L, 1);
17✔
494
  return 1;
17✔
495
}
496
#endif /* !LJ_TARGET_WINDOWS */
497

498
#include "lj_libdef.h"
499

500
/* ------------------------------------------------------------------------ */
501

502
LUALIB_API int luaopen_misc(struct lua_State *L)
367✔
503
{
504
#if !LJ_TARGET_WINDOWS
505
  luaM_sysprof_set_writer(buffer_writer_default);
367✔
506
  luaM_sysprof_set_on_stop(on_stop_cb_default);
367✔
507
  /*
508
  ** XXX: Passing NULL to the backtracer configuration handle sets the default
509
  ** backtracing function.
510
  */
511
  luaM_sysprof_set_backtracer(NULL);
367✔
512
#endif /* !LJ_TARGET_WINDOWS */
513

514
  LJ_LIB_REG(L, LUAM_MISCLIBNAME, misc);
367✔
515
#if !LJ_TARGET_WINDOWS
516
  LJ_LIB_REG(L, LUAM_MISCLIBNAME ".memprof", misc_memprof);
367✔
517
  LJ_LIB_REG(L, LUAM_MISCLIBNAME ".sysprof", misc_sysprof);
367✔
518
#endif /* !LJ_TARGET_WINDOWS */
519
  return 1;
367✔
520
}
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