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

tarantool / luajit / 13700713436

06 Mar 2025 02:08PM UTC coverage: 93.017% (+0.01%) from 93.003%
13700713436

push

github

ligurio
sysprof: fix a message with stop without run

The function `misc.sysprof.stop()` reports that profiler is
already running:

| $ ./src/luajit -e 'print(misc.sysprof.stop())'
| nil     profiler is running already     22

both in `sysprof_error()` and fixes aforementioned problem.

Follows up tarantool/tarantool#781

5700 of 6038 branches covered (94.4%)

Branch coverage included in aggregate %.

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

13 existing lines in 4 files now uncovered.

21726 of 23447 relevant lines covered (92.66%)

2961122.38 hits per line

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

93.0
/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,
28,921✔
34
                                   const char *name, int64_t val)
35
{
36
  setnumV(lj_tab_setstr(L, t, lj_str_newz(L, name)), (double)val);
28,921✔
37
}
38

39
#define LJLIB_MODULE_misc
40

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

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

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

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

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

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

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

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

75
  return 1;
1,521✔
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. */
120
      if (errno != EINTR) {
×
121
  /* Will be freed as whole chunk later. */
122
  *buf_addr = NULL;
×
123
  return write_total;
×
124
      }
125

126
      errno = 0;
×
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

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✔
187
    opt->mode = LUAM_SYSPROF_DEFAULT;
×
188
    opt->interval = SYSPROF_DEFAULT_INTERVAL;
×
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✔
202
        *err_details = err2msg(LJ_ERR_PROF_DETAILS_BADMODE);
×
203
        return PROFILE_ERRUSE;
×
204
      }
205
      mode = strVdata(mode_opt);
17✔
206
      if (strlen(mode) == 0 || mode[1] != '\0') {
17✔
207
        *err_details = err2msg(LJ_ERR_PROF_DETAILS_BADMODE);
×
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 prof_error(lua_State *L, int status, const char *err_details)
10✔
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 || LJ_HASMEMPROF
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 prof_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 prof_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 prof_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 prof_error(L, PROFILE_ERRUSE, err_details);
337
#else
338
  int status = luaM_sysprof_stop(L);
11✔
339
  if (LJ_UNLIKELY(status == PROFILE_ERRRUN)) {
11✔
340
      lua_pushnil(L);
1✔
341
      lua_pushstring(L, err2msg(LJ_ERR_PROF_NOTRUNNING));
1✔
342
      lua_pushinteger(L, EINVAL);
1✔
343
      return 3;
1✔
344
  }
345
  if (LJ_UNLIKELY(status != PROFILE_SUCCESS))
10✔
UNCOV
346
    return prof_error(L, status, NULL);
×
347

348
  lua_pushboolean(L, 1);
10✔
349
  return 1;
10✔
350
#endif /* !LJ_HASSYSPROF */
351
}
352

353
/* local counters, err, errno = sysprof.report() */
354
LJLIB_CF(misc_sysprof_report)
2✔
355
{
356
#if !LJ_HASSYSPROF
357
  const char *err_details = err2msg(LJ_ERR_PROF_DETAILS_DISABLED);
358
  return prof_error(L, PROFILE_ERRUSE, err_details);
359
#else
360
  struct luam_Sysprof_Counters counters = {};
2✔
361
  GCtab *data_tab = NULL;
2✔
362
  GCtab *count_tab = NULL;
2✔
363
  int status = luaM_sysprof_report(&counters);
2✔
364
  if (status != PROFILE_SUCCESS)
2✔
UNCOV
365
    return prof_error(L, status, NULL);
×
366

367
  lua_createtable(L, 0, 3);
2✔
368
  data_tab = tabV(L->top - 1);
2✔
369

370
  setnumfield(L, data_tab, "samples", counters.samples);
2✔
371

372
  lua_createtable(L, 0, LJ_VMST__MAX + 1);
2✔
373
  count_tab = tabV(L->top - 1);
2✔
374

375
  setnumfield(L, count_tab, "INTERP", counters.vmst_interp);
2✔
376
  setnumfield(L, count_tab, "LFUNC",  counters.vmst_lfunc);
2✔
377
  setnumfield(L, count_tab, "FFUNC",  counters.vmst_ffunc);
2✔
378
  setnumfield(L, count_tab, "CFUNC",  counters.vmst_cfunc);
2✔
379
  setnumfield(L, count_tab, "GC",     counters.vmst_gc);
2✔
380
  setnumfield(L, count_tab, "EXIT",   counters.vmst_exit);
2✔
381
  setnumfield(L, count_tab, "RECORD", counters.vmst_record);
2✔
382
  setnumfield(L, count_tab, "OPT",    counters.vmst_opt);
2✔
383
  setnumfield(L, count_tab, "ASM",    counters.vmst_asm);
2✔
384
  setnumfield(L, count_tab, "TRACE",  counters.vmst_trace);
2✔
385

386
  lua_setfield(L, -2, "vmstate");
2✔
387

388
  return 1;
2✔
389
#endif /* !LJ_HASSYSPROF */
390
}
391

392
/* ----- misc.memprof module ---------------------------------------------- */
393

394
#define LJLIB_MODULE_misc_memprof
395

396
#define MEMPROF_DEFAULT_OUTPUT "memprof.bin"
397

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

411
  /*
412
  ** FIXME: more elegant solution with ctx.
413
  ** Throws in case of OOM.
414
  */
415
  ctx = lj_mem_new(L, sizeof(*ctx));
19✔
416
  opt.ctx = ctx;
19✔
417
  opt.buf = ctx->buf;
19✔
418
  opt.writer = buffer_writer_default;
19✔
419
  opt.on_stop = on_stop_cb_default;
19✔
420
  opt.len = STREAM_BUFFER_SIZE;
19✔
421

422
  ctx->g = G(L);
19✔
423
  ctx->fd = open(fname, O_CREAT | O_WRONLY | O_TRUNC, 0644);
19✔
424

425
  if (ctx->fd == -1) {
19✔
426
    lj_mem_free(ctx->g, ctx, sizeof(*ctx));
1✔
427
    return prof_error(L, PROFILE_ERRIO, fname);
1✔
428
  }
429

430
  memprof_status = lj_memprof_start(L, &opt);
18✔
431
  if (LJ_UNLIKELY(memprof_status != PROFILE_SUCCESS))
18✔
432
    return prof_error(L, memprof_status, NULL);
1✔
433

434
  lua_pushboolean(L, 1);
17✔
435
  return 1;
17✔
436
#endif /* !LJ_HASMEMPROF */
437
}
438

439
/* local stopped, err, errno = misc.memprof.stop() */
440
LJLIB_CF(misc_memprof_stop)
18✔
441
{
442
#if !LJ_HASMEMPROF
443
  const char *err_details = err2msg(LJ_ERR_PROF_DETAILS_DISABLED);
444
  return prof_error(L, PROFILE_ERRUSE, err_details);
445
#else
446
  int status = lj_memprof_stop(L);
18✔
447
  if (LJ_UNLIKELY(status == PROFILE_ERRRUN)) {
18✔
448
      lua_pushnil(L);
1✔
449
      lua_pushstring(L, err2msg(LJ_ERR_PROF_NOTRUNNING));
1✔
450
      lua_pushinteger(L, EINVAL);
1✔
451
      return 3;
1✔
452
  }
453
  if (LJ_UNLIKELY(status != PROFILE_SUCCESS))
17✔
UNCOV
454
    return prof_error(L, status, NULL);
×
455

456
  lua_pushboolean(L, 1);
17✔
457
  return 1;
17✔
458
#endif /* !LJ_HASMEMPROF */
459
}
460
#endif /* !LJ_TARGET_WINDOWS */
461

462
#include "lj_libdef.h"
463

464
/* ------------------------------------------------------------------------ */
465

466
LUALIB_API int luaopen_misc(struct lua_State *L)
370✔
467
{
468
#if !LJ_TARGET_WINDOWS
469
  luaM_sysprof_set_writer(buffer_writer_default);
370✔
470
  luaM_sysprof_set_on_stop(on_stop_cb_default);
370✔
471
  /*
472
  ** XXX: Passing NULL to the backtracer configuration handle sets the default
473
  ** backtracing function.
474
  */
475
  luaM_sysprof_set_backtracer(NULL);
370✔
476
#endif /* !LJ_TARGET_WINDOWS */
477

478
  LJ_LIB_REG(L, LUAM_MISCLIBNAME, misc);
370✔
479
#if !LJ_TARGET_WINDOWS
480
  LJ_LIB_REG(L, LUAM_MISCLIBNAME ".memprof", misc_memprof);
370✔
481
  LJ_LIB_REG(L, LUAM_MISCLIBNAME ".sysprof", misc_sysprof);
370✔
482
#endif /* !LJ_TARGET_WINDOWS */
483
  return 1;
370✔
484
}
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