Coverage Report

Created: 2023-11-27 07:15

/src/tarantool/third_party/luajit/src/lib_misc.c
Line
Count
Source (jump to first uncovered line)
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,
33
           const char *name, int64_t val)
34
0
{
35
0
  setnumV(lj_tab_setstr(L, t, lj_str_newz(L, name)), (double)val);
36
0
}
37
38
#define LJLIB_MODULE_misc
39
40
LJLIB_CF(misc_getmetrics)
41
0
{
42
0
  struct luam_Metrics metrics;
43
0
  GCtab *m;
44
45
0
  lua_createtable(L, 0, 19);
46
0
  m = tabV(L->top - 1);
47
48
0
  luaM_metrics(L, &metrics);
49
50
0
  setnumfield(L, m, "strhash_hit", metrics.strhash_hit);
51
0
  setnumfield(L, m, "strhash_miss", metrics.strhash_miss);
52
53
0
  setnumfield(L, m, "gc_strnum", metrics.gc_strnum);
54
0
  setnumfield(L, m, "gc_tabnum", metrics.gc_tabnum);
55
0
  setnumfield(L, m, "gc_udatanum", metrics.gc_udatanum);
56
0
  setnumfield(L, m, "gc_cdatanum", metrics.gc_cdatanum);
57
58
0
  setnumfield(L, m, "gc_total", metrics.gc_total);
59
0
  setnumfield(L, m, "gc_freed", metrics.gc_freed);
60
0
  setnumfield(L, m, "gc_allocated", metrics.gc_allocated);
61
62
0
  setnumfield(L, m, "gc_steps_pause", metrics.gc_steps_pause);
63
0
  setnumfield(L, m, "gc_steps_propagate", metrics.gc_steps_propagate);
64
0
  setnumfield(L, m, "gc_steps_atomic", metrics.gc_steps_atomic);
65
0
  setnumfield(L, m, "gc_steps_sweepstring", metrics.gc_steps_sweepstring);
66
0
  setnumfield(L, m, "gc_steps_sweep", metrics.gc_steps_sweep);
67
0
  setnumfield(L, m, "gc_steps_finalize", metrics.gc_steps_finalize);
68
69
0
  setnumfield(L, m, "jit_snap_restore", metrics.jit_snap_restore);
70
0
  setnumfield(L, m, "jit_trace_abort", metrics.jit_trace_abort);
71
0
  setnumfield(L, m, "jit_mcode_size", metrics.jit_mcode_size);
72
0
  setnumfield(L, m, "jit_trace_num", metrics.jit_trace_num);
73
74
0
  return 1;
75
0
}
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
0
#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,
104
            void *opt)
105
0
{
106
0
  struct profile_ctx *ctx = opt;
107
0
  const int fd = ctx->fd;
108
0
  const void * const buf_start = *buf_addr;
109
0
  const void *data = *buf_addr;
110
0
  size_t write_total = 0;
111
112
0
  lj_assertX(len <= STREAM_BUFFER_SIZE, "stream buffer overflow");
113
114
0
  for (;;) {
115
0
    const ssize_t written = write(fd, data, len - write_total);
116
117
0
    if (LJ_UNLIKELY(written == -1)) {
118
      /* Re-tries write in case of EINTR. */
119
0
      if (errno != EINTR) {
120
  /* Will be freed as whole chunk later. */
121
0
  *buf_addr = NULL;
122
0
  return write_total;
123
0
      }
124
125
0
      errno = 0;
126
0
      continue;
127
0
    }
128
129
0
    write_total += written;
130
0
    lj_assertX(write_total <= len, "invalid stream buffer write");
131
132
0
    if (write_total == len)
133
0
      break;
134
135
0
    data = (uint8_t *)data + (ptrdiff_t)written;
136
0
  }
137
138
0
  *buf_addr = buf_start;
139
0
  return write_total;
140
0
}
141
142
/* Default on stop callback. Just close the corresponding descriptor. */
143
static int on_stop_cb_default(void *opt, uint8_t *buf)
144
0
{
145
0
  struct profile_ctx *ctx = NULL;
146
0
  int fd = 0;
147
148
0
  if (opt == NULL) {
149
    /* Nothing to do. */
150
0
    return 0;
151
0
  }
152
153
0
  ctx = opt;
154
0
  fd = ctx->fd;
155
0
  UNUSED(buf);
156
0
  lj_mem_free(ctx->g, ctx, sizeof(*ctx));
157
0
  return close(fd);
158
0
}
159
160
/* ----- misc.sysprof module ---------------------------------------------- */
161
162
#define LJLIB_MODULE_misc_sysprof
163
164
/* The default profiling interval equals to 11 ms. */
165
0
#define SYSPROF_DEFAULT_INTERVAL 10
166
0
#define SYSPROF_DEFAULT_OUTPUT "sysprof.bin"
167
168
0
static int set_output_path(const char *path, struct luam_Sysprof_Options *opt) {
169
0
  struct profile_ctx *ctx = opt->ctx;
170
0
  int fd = 0;
171
0
  lj_assertX(path != NULL, "no file to open by sysprof");
172
0
  fd = open(path, O_CREAT | O_WRONLY | O_TRUNC, 0644);
173
0
  if(fd == -1) {
174
0
    return PROFILE_ERRIO;
175
0
  }
176
0
  ctx->fd = fd;
177
0
  return PROFILE_SUCCESS;
178
0
}
179
180
0
static int parse_sysprof_opts(lua_State *L, struct luam_Sysprof_Options *opt, int idx) {
181
0
  GCtab *options = lj_lib_checktab(L, idx);
182
183
  /* Get profiling mode. */
184
0
  {
185
0
    const char *mode = NULL;
186
187
0
    cTValue *mode_opt = lj_tab_getstr(options, lj_str_newlit(L, "mode"));
188
0
    if (!mode_opt || !tvisstr(mode_opt)) {
189
0
      return PROFILE_ERRUSE;
190
0
    }
191
192
0
    mode = strVdata(mode_opt);
193
0
    if (mode[1] != '\0')
194
0
      return PROFILE_ERRUSE;
195
196
0
    switch (*mode) {
197
0
      case 'D':
198
0
        opt->mode = LUAM_SYSPROF_DEFAULT;
199
0
        break;
200
0
      case 'L':
201
0
        opt->mode = LUAM_SYSPROF_LEAF;
202
0
        break;
203
0
      case 'C':
204
0
        opt->mode = LUAM_SYSPROF_CALLGRAPH;
205
0
        break;
206
0
      default:
207
0
        return PROFILE_ERRUSE;
208
0
    }
209
0
  }
210
211
  /* Get profiling interval. */
212
0
  {
213
0
    cTValue *interval = lj_tab_getstr(options, lj_str_newlit(L, "interval"));
214
0
    opt->interval = SYSPROF_DEFAULT_INTERVAL;
215
0
    if (interval && tvisnumber(interval)) {
216
0
      int32_t signed_interval = numberVint(interval);
217
0
      if (signed_interval < 1)
218
0
        return PROFILE_ERRUSE;
219
0
      opt->interval = signed_interval;
220
0
    }
221
0
  }
222
223
  /* Get output path. */
224
0
  if (opt->mode != LUAM_SYSPROF_DEFAULT)
225
0
  {
226
0
    const char *path = NULL;
227
0
    struct profile_ctx *ctx = NULL;
228
0
    int status = 0;
229
230
0
    cTValue *pathtv = lj_tab_getstr(options, lj_str_newlit(L, "path"));
231
0
    if (!pathtv)
232
0
      path = SYSPROF_DEFAULT_OUTPUT;
233
0
    else if (!tvisstr(pathtv))
234
0
      return PROFILE_ERRUSE;
235
0
    else
236
0
      path = strVdata(pathtv);
237
238
0
    ctx = lj_mem_new(L, sizeof(*ctx));
239
0
    ctx->g = G(L);
240
0
    opt->ctx = ctx;
241
0
    opt->buf = ctx->buf;
242
0
    opt->len = STREAM_BUFFER_SIZE;
243
244
0
    status = set_output_path(path, opt);
245
0
    if (status != PROFILE_SUCCESS) {
246
0
      lj_mem_free(ctx->g, ctx, sizeof(*ctx));
247
0
      return status;
248
0
    }
249
0
  }
250
251
0
  return PROFILE_SUCCESS;
252
0
}
253
254
static int parse_options(lua_State *L, struct luam_Sysprof_Options *opt)
255
0
{
256
0
  if (lua_gettop(L) != 1)
257
0
    return PROFILE_ERRUSE;
258
259
0
  if (!lua_istable(L, 1))
260
0
    return PROFILE_ERRUSE;
261
262
0
  return parse_sysprof_opts(L, opt, 1);
263
0
}
264
265
static int sysprof_error(lua_State *L, int status)
266
0
{
267
0
  switch (status) {
268
0
    case PROFILE_ERRUSE:
269
0
      lua_pushnil(L);
270
0
      lua_pushstring(L, err2msg(LJ_ERR_PROF_MISUSE));
271
0
      lua_pushinteger(L, EINVAL);
272
0
      return 3;
273
0
#if LJ_HASSYSPROF
274
0
    case PROFILE_ERRRUN:
275
0
      lua_pushnil(L);
276
0
      lua_pushstring(L, err2msg(LJ_ERR_PROF_ISRUNNING));
277
0
      lua_pushinteger(L, EINVAL);
278
0
      return 3;
279
0
    case PROFILE_ERRIO:
280
0
      return luaL_fileresult(L, 0, NULL);
281
0
#endif
282
0
    default:
283
0
      lj_assertL(0, "bad sysprof error %d", status);
284
0
      return 0;
285
0
  }
286
0
}
287
288
/* local res, err, errno = sysprof.start(options) */
289
LJLIB_CF(misc_sysprof_start)
290
0
{
291
0
  int status = PROFILE_SUCCESS;
292
293
0
  struct luam_Sysprof_Options opt = {};
294
295
0
  status = parse_options(L, &opt);
296
0
  if (LJ_UNLIKELY(status != PROFILE_SUCCESS))
297
0
    return sysprof_error(L, status);
298
299
0
  status = luaM_sysprof_start(L, &opt);
300
0
  if (LJ_UNLIKELY(status != PROFILE_SUCCESS))
301
    /* Allocated memory will be freed in on_stop callback. */
302
0
    return sysprof_error(L, status);
303
304
0
  lua_pushboolean(L, 1);
305
0
  return 1;
306
0
}
307
308
/* local res, err, errno = profile.sysprof_stop() */
309
LJLIB_CF(misc_sysprof_stop)
310
0
{
311
0
  int status = luaM_sysprof_stop(L);
312
0
  if (LJ_UNLIKELY(status != PROFILE_SUCCESS))
313
0
    return sysprof_error(L, status);
314
315
0
  lua_pushboolean(L, 1);
316
0
  return 1;
317
0
}
318
319
/* local counters, err, errno = sysprof.report() */
320
LJLIB_CF(misc_sysprof_report)
321
0
{
322
0
  struct luam_Sysprof_Counters counters = {};
323
0
  GCtab *data_tab = NULL;
324
0
  GCtab *count_tab = NULL;
325
326
0
  int status = luaM_sysprof_report(&counters);
327
0
  if (status != PROFILE_SUCCESS)
328
0
    return sysprof_error(L, status);
329
330
0
  lua_createtable(L, 0, 3);
331
0
  data_tab = tabV(L->top - 1);
332
333
0
  setnumfield(L, data_tab, "samples", counters.samples);
334
335
0
  lua_createtable(L, 0, LJ_VMST__MAX + 1);
336
0
  count_tab = tabV(L->top - 1);
337
338
0
  setnumfield(L, count_tab, "INTERP", counters.vmst_interp);
339
0
  setnumfield(L, count_tab, "LFUNC",  counters.vmst_lfunc);
340
0
  setnumfield(L, count_tab, "FFUNC",  counters.vmst_ffunc);
341
0
  setnumfield(L, count_tab, "CFUNC",  counters.vmst_cfunc);
342
0
  setnumfield(L, count_tab, "GC",     counters.vmst_gc);
343
0
  setnumfield(L, count_tab, "EXIT",   counters.vmst_exit);
344
0
  setnumfield(L, count_tab, "RECORD", counters.vmst_record);
345
0
  setnumfield(L, count_tab, "OPT",    counters.vmst_opt);
346
0
  setnumfield(L, count_tab, "ASM",    counters.vmst_asm);
347
0
  setnumfield(L, count_tab, "TRACE",  counters.vmst_trace);
348
349
0
  lua_setfield(L, -2, "vmstate");
350
351
0
  return 1;
352
0
}
353
354
/* ----- misc.memprof module ---------------------------------------------- */
355
356
#define LJLIB_MODULE_misc_memprof
357
358
/* local started, err, errno = misc.memprof.start(fname) */
359
LJLIB_CF(misc_memprof_start)
360
0
{
361
0
  struct lj_memprof_options opt = {0};
362
0
  const char *fname = strdata(lj_lib_checkstr(L, 1));
363
0
  struct profile_ctx *ctx;
364
0
  int memprof_status;
365
366
  /*
367
  ** FIXME: more elegant solution with ctx.
368
  ** Throws in case of OOM.
369
  */
370
0
  ctx = lj_mem_new(L, sizeof(*ctx));
371
0
  opt.ctx = ctx;
372
0
  opt.buf = ctx->buf;
373
0
  opt.writer = buffer_writer_default;
374
0
  opt.on_stop = on_stop_cb_default;
375
0
  opt.len = STREAM_BUFFER_SIZE;
376
377
0
  ctx->g = G(L);
378
0
  ctx->fd = open(fname, O_CREAT | O_WRONLY | O_TRUNC, 0644);
379
380
0
  if (ctx->fd == -1) {
381
0
    lj_mem_free(ctx->g, ctx, sizeof(*ctx));
382
0
    return luaL_fileresult(L, 0, fname);
383
0
  }
384
385
0
  memprof_status = lj_memprof_start(L, &opt);
386
387
0
  if (LJ_UNLIKELY(memprof_status != PROFILE_SUCCESS)) {
388
0
    switch (memprof_status) {
389
0
    case PROFILE_ERRUSE:
390
0
      lua_pushnil(L);
391
0
      lua_pushstring(L, err2msg(LJ_ERR_PROF_MISUSE));
392
0
      lua_pushinteger(L, EINVAL);
393
0
      return 3;
394
0
#if LJ_HASMEMPROF
395
0
    case PROFILE_ERRRUN:
396
0
      lua_pushnil(L);
397
0
      lua_pushstring(L, err2msg(LJ_ERR_PROF_ISRUNNING));
398
0
      lua_pushinteger(L, EINVAL);
399
0
      return 3;
400
0
    case PROFILE_ERRIO:
401
0
      return luaL_fileresult(L, 0, fname);
402
0
#endif
403
0
    default:
404
0
      lj_assertL(0, "bad memprof error %d", memprof_status);
405
0
      return 0;
406
0
    }
407
0
  }
408
0
  lua_pushboolean(L, 1);
409
0
  return 1;
410
0
}
411
412
/* local stopped, err, errno = misc.memprof.stop() */
413
LJLIB_CF(misc_memprof_stop)
414
0
{
415
0
  int status = lj_memprof_stop(L);
416
0
  if (status != PROFILE_SUCCESS) {
417
0
    switch (status) {
418
0
    case PROFILE_ERRUSE:
419
0
      lua_pushnil(L);
420
0
      lua_pushstring(L, err2msg(LJ_ERR_PROF_MISUSE));
421
0
      lua_pushinteger(L, EINVAL);
422
0
      return 3;
423
0
#if LJ_HASMEMPROF
424
0
    case PROFILE_ERRRUN:
425
0
      lua_pushnil(L);
426
0
      lua_pushstring(L, err2msg(LJ_ERR_PROF_NOTRUNNING));
427
0
      lua_pushinteger(L, EINVAL);
428
0
      return 3;
429
0
    case PROFILE_ERRIO:
430
0
      return luaL_fileresult(L, 0, NULL);
431
0
#endif
432
0
    default:
433
0
      lj_assertL(0, "bad memprof error %d", status);
434
0
      return 0;
435
0
    }
436
0
  }
437
0
  lua_pushboolean(L, 1);
438
0
  return 1;
439
0
}
440
#endif /* !LJ_TARGET_WINDOWS */
441
442
#include "lj_libdef.h"
443
444
/* ------------------------------------------------------------------------ */
445
446
LUALIB_API int luaopen_misc(struct lua_State *L)
447
2.60k
{
448
2.60k
#if !LJ_TARGET_WINDOWS
449
2.60k
  luaM_sysprof_set_writer(buffer_writer_default);
450
2.60k
  luaM_sysprof_set_on_stop(on_stop_cb_default);
451
  /*
452
  ** XXX: Passing NULL to the backtracer configuration handle sets the default
453
  ** backtracing function.
454
  */
455
2.60k
  luaM_sysprof_set_backtracer(NULL);
456
2.60k
#endif /* !LJ_TARGET_WINDOWS */
457
458
2.60k
  LJ_LIB_REG(L, LUAM_MISCLIBNAME, misc);
459
2.60k
#if !LJ_TARGET_WINDOWS
460
2.60k
  LJ_LIB_REG(L, LUAM_MISCLIBNAME ".memprof", misc_memprof);
461
2.60k
  LJ_LIB_REG(L, LUAM_MISCLIBNAME ".sysprof", misc_sysprof);
462
2.60k
#endif /* !LJ_TARGET_WINDOWS */
463
2.60k
  return 1;
464
2.60k
}