Tarantool development patches archive
 help / color / mirror / Atom feed
From: Sergey Kaplun via Tarantool-patches <tarantool-patches@dev.tarantool.org>
To: Maxim Kokryashkin <max.kokryashkin@gmail.com>
Cc: tarantool-patches@dev.tarantool.org
Subject: Re: [Tarantool-patches] [PATCH luajit 6/7] sysprof: introduce Lua API
Date: Tue, 5 Oct 2021 18:36:06 +0300	[thread overview]
Message-ID: <YVxw5keiLEwsCWS+@root> (raw)
In-Reply-To: <7fac25ca504796c478cf5ba51f65631378bc8c4f.1631122521.git.m.kokryashkin@tarantool.org>

Hi, Maxim!

Thanks for the patch!

Please consider my comments below.

On 08.09.21, Maxim Kokryashkin wrote:
> This commit introduces Lua API for sysprof, so it is now possible to
> call it from anywhere in a Lua script. All of the parameters are passed
> inside a single Lua table.
> The following options are supported:
> - mode:     "D" (Default mode, only virtual machine state counters)
>             "L" (Leaf mode, shows the last frame on the stack)
>             "C" (Callchain mode, full stack dump)
> - interval: time between samples

Please add note that this is time in miliseconds.

> - path:     Path to file in which profiling data should be stored

Minor: It would be nice to mention the return values.

> 
> Part of tarantool/tarantool#781
> ---
>  src/lib_misc.c                                | 224 ++++++++++++++++++
>  .../misclib-sysprof-lapi.test.lua             | 105 ++++++++
>  2 files changed, 329 insertions(+)
>  create mode 100644 test/tarantool-tests/misclib-sysprof-lapi.test.lua
> 
> diff --git a/src/lib_misc.c b/src/lib_misc.c
> index a44476e0..a3423cdd 100644
> --- a/src/lib_misc.c
> +++ b/src/lib_misc.c

I have the following warnings when compile with
| -DCMAKE_C_FLAGS="-Wextra -Wdeclaration-after-statement -Wredundant-decls -Wshadow -Wpointer-arith"

| /home/burii/reviews/luajit/sysprof/src/lib_misc.c: In function 'set_output_path':
| /home/burii/reviews/luajit/sysprof/src/lib_misc.c:201:3: warning: ISO C90 forbids mixed declarations and code [-Wdeclaration-after-statement]
|   201 |   struct profile_ctx *ctx = opt->ctx;
|       |   ^~~~~~
| /home/burii/reviews/luajit/sysprof/src/lib_misc.c: In function 'parse_output_path':
| /home/burii/reviews/luajit/sysprof/src/lib_misc.c: In function 'set_output_path':
| /home/burii/reviews/luajit/sysprof/src/lib_misc.c:201:3: warning: ISO C90 forbids mixed declarations and code [-Wdeclaration-after-statement]
|   201 |   struct profile_ctx *ctx = opt->ctx;
|       |   ^~~~~~
| /home/burii/reviews/luajit/sysprof/src/lib_misc.c: In function 'parse_output_path':
| /home/burii/reviews/luajit/sysprof/src/lib_misc.c:210:74: warning: unused parameter 'opt' [-Wunused-parameter]
|   210 | const char* parse_output_path(lua_State *L, struct luam_sysprof_options *opt, int idx) {
|       |                                             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~
| /home/burii/reviews/luajit/sysprof/src/lib_misc.c: In function 'parse_options':
| /home/burii/reviews/luajit/sysprof/src/lib_misc.c:226:7: warning: ISO C90 forbids mixed declarations and code [-Wdeclaration-after-statement]
|   226 |       const char* path = parse_output_path(L, opt, 1);
|       |       ^~~~~
| /home/burii/reviews/luajit/sysprof/src/lib_misc.c: In function 'lj_cf_misc_sysprof_start':
| /home/burii/reviews/luajit/sysprof/src/lib_misc.c:296:3: warning: ISO C90 forbids mixed declarations and code [-Wdeclaration-after-statement]
|   296 |   const struct luam_sysprof_config conf = {buffer_writer_default, on_stop_cb_default, NULL};
|       |   ^~~~~
| /home/burii/reviews/luajit/sysprof/src/lib_misc.c:210:74: warning: unused parameter 'opt' [-Wunused-parameter]
|   210 | const char* parse_output_path(lua_State *L, struct luam_sysprof_options *opt, int idx) {
|       |                                             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~
| /home/burii/reviews/luajit/sysprof/src/lib_misc.c: In function 'parse_options':
| /home/burii/reviews/luajit/sysprof/src/lib_misc.c:226:7: warning: ISO C90 forbids mixed declarations and code [-Wdeclaration-after-statement]
|   226 |       const char* path = parse_output_path(L, opt, 1);
|       |       ^~~~~
| /home/burii/reviews/luajit/sysprof/src/lib_misc.c: In function 'lj_cf_misc_sysprof_start':
| /home/burii/reviews/luajit/sysprof/src/lib_misc.c:296:3: warning: ISO C90 forbids mixed declarations and code [-Wdeclaration-after-statement]
|   296 |   const struct luam_sysprof_config conf = {buffer_writer_default, on_stop_cb_default, NULL};
|       |   ^~~~~

> @@ -145,6 +145,218 @@ static int on_stop_cb_default(void *opt, uint8_t *buf)
>    return fclose(stream);
>  }
>  
> +/* ----- misc.sysprof module ---------------------------------------------- */
> +
> +/* Not loaded by default, use: local profile = require("misc.sysprof") */

Looks like it is not true. See example here [1].
Also, I see no reason for differnce between sysprof and memprof here.

> +#define LJLIB_MODULE_misc_sysprof
> +
> +/* The default profiling interval equals to 11 ms. */
> +#define SYSPROF_DEFAULT_INTERVAL (11)

Why 11 and not 10, 9 or something else?
Nit: () are excess

> +#define SYSPROF_DEFAULT_OUTPUT "sysprof.bin"

I suppose that it is better if user makes a mistake in any parameter you
should raise an error to inform him.

> +
> +int parse_sysprof_opts(lua_State *L, struct luam_sysprof_options *opt, int idx) {
> +  GCtab *options = lj_lib_checktab(L, idx);

Looks like the index is used only here, so we can check this outside
this function. Also, I'm a little bit frightened by the difference with
memprof. User may set this parameter in the 1st argument and in the
opttable, what we should prefer to use?

I suggest to similarize API with memprof's -- so the first argument is
always a string contains the filename for profiler's output. The second
is a table with others options (as we agreed offline, but I still think
that it is not Lua-style API) or nil. If the second argument is nil we
still can run profiler with default options (i.e. interval and mode "D"
as the most harmless). If the option has the wrong type (a function or
a table, etc. instead a string) we should raise an error.

If the second argument is the table with only "mode" or only "interval"
key is set we use the default value for another. If there is any unknown
option (for example "inteval") we must raise an error (unknown option),
to notify the user that he tries to use a wrong option in the table.

OTOH, I don't know how the API should look when we profiling in
Default mode, i.e. without any output to file. So maybe it is better
always expect the table as the first argument. When the profiling mode
is Default we should raise and error that, path argument is invalid.

Thoughts?

> +
> +  /* get profiling mode */

Typo: s/get/Get/ s/mode/mode./

> +  {
> +    cTValue *mode_opt = lj_tab_getstr(options, lj_str_newlit(L, "mode"));
> +    char mode = 0;
> +    if (mode_opt) {

Nit: {} are excess.

> +      mode = *strVdata(mode_opt);

There is no guarantee that the value is a string, so the behaviour is
undefined.

> +    }

Default mode is not taking into account here, see the comment above.

> +
> +    switch (mode) {
> +      case 'D':
> +        opt->mode = LUAM_SYSPROF_DEFAULT;

Nit: We use quater-half tabs code style in LuaJIT.
So 8spaces should be replased with a single tab.
Here and below.

> +        break;
> +      case 'L':
> +        opt->mode = LUAM_SYSPROF_LEAF;
> +        break;
> +      case 'C':
> +        opt->mode = LUAM_SYSPROF_CALLGRAPH;
> +        break;
> +      default:
> +        return SYSPROF_ERRUSE;

What is the behaviour in case when mode is "CDL", and when is "DCL"?

> +    }
> +  }
> +
> +  /* Get profiling interval. */
> +  {
> +    cTValue *interval = lj_tab_getstr(options, lj_str_newlit(L, "interval"));
> +    opt->interval = SYSPROF_DEFAULT_INTERVAL;
> +    if (interval) {
> +      int32_t signed_interval = numberVint(interval);

There is no guarantee that interval TValue is a number or string, we
should check it.

| $ src/luajit -e 'misc.sysprof.start( {mode = "C", interval = function() end, path = "/tmp/sysprof.bin"})'
| luajit: /home/burii/reviews/luajit/sysprof/src/lj_obj.h:1025: numberVint: Assertion `(((o)->it) < 0xfffeffffu)' failed.
| Aborted

> +      if (signed_interval < 1) {

Nit: {} are excess.

> +        return SYSPROF_ERRUSE;
> +      }
> +      opt->interval = signed_interval;
> +    }
> +  }
> +
> +  return SYSPROF_SUCCESS;

According to RFC [2] we can use set path via this table, however the
corresponding check is omited.

| local started, err, errno = misc.sysprof.start{
|   mode = profiling_mode,
|   interval = sampling_interval,
|   path = fname,
| }

But, as I said, for more consistency with memprof we can drop path from
options table.

> +}
> +
> +int set_output_path(const char* path, struct luam_sysprof_options *opt) {

Typo: s/char* path/char *path/

> +  lua_assert(path != NULL);
> +  struct profile_ctx *ctx = opt->ctx;
> +  FILE *stream = fopen(path, "wb");
> +  if(!stream) {
> +    return SYSPROF_ERRIO;
> +  }
> +  ctx->stream = stream;
> +  return SYSPROF_SUCCESS;
> +}

I don't understand this fucntion's destiny. Why we can't just use fopen
and check than returned value isn't NULL?

> +
> +const char* parse_output_path(lua_State *L, struct luam_sysprof_options *opt, int idx) {
> +  /* By default, sysprof writes events to a `sysprof.bin` file. */

See comment about default file above.

> +  const char *path = luaL_checkstring(L, idx);
> +  return path ? path : SYSPROF_DEFAULT_OUTPUT;
> +}
> +
> +int parse_options(lua_State *L, struct luam_sysprof_options *opt)
> +{
> +  int status = SYSPROF_SUCCESS;
> +
> +  switch(lua_gettop(L)) {

See comment about Lua API above.

> +    case 2:
> +      if(!(lua_isstring(L, 1) && lua_istable(L, 2))) {
> +        status = SYSPROF_ERRUSE;
> +        break;
> +      }
> +      const char* path = parse_output_path(L, opt, 1); 
                                                         ^
Minor: Trailing whitespace here.-------------------------^

> +      status = set_output_path(path, opt);
> +      if(status != SYSPROF_SUCCESS) 
                                      ^
Minor: Trailing whitespace here.------^

> +        break;
> +
> +      status = parse_sysprof_opts(L, opt, 2);
> +      break;
> +    case 1:
> +      if(!lua_istable(L, 1)) {
> +        status = SYSPROF_ERRUSE;
> +        break;
> +      }
> +      status = parse_sysprof_opts(L, opt, 1);
> +      if(status != SYSPROF_SUCCESS)
> +        break;
> +      status = set_output_path(SYSPROF_DEFAULT_OUTPUT, opt);
> +      break;
> +    default:
> +      status = SYSPROF_ERRUSE;
> +      break;
> +  } 
      ^
Minor: Trailing whitespace here.

> +  return status;
> +}
> +
> +int sysprof_error(lua_State *L, int status)
> +{
> +  switch (status) {
> +    case SYSPROF_ERRUSE:
> +      lua_pushnil(L);
> +      lua_pushstring(L, err2msg(LJ_ERR_PROF_MISUSE));
> +      lua_pushinteger(L, EINVAL);
> +      return 3;
> +    case SYSPROF_ERRRUN:

LJ_ERR_PROF_ISRUNNING and LJ_ERR_PROF_NOTRUNNING is defined only, when
LJ_HASMEMPROF || LJ_HASSYSPROF, so it must be the corresponding ifdef
here.

> +      lua_pushnil(L);
> +      lua_pushstring(L, err2msg(LJ_ERR_PROF_ISRUNNING));
> +      lua_pushinteger(L, EINVAL);
> +      return 3;
> +    case SYSPROF_ERRSTOP:
> +      lua_pushnil(L);
> +      lua_pushstring(L, err2msg(LJ_ERR_PROF_NOTRUNNING));
> +      lua_pushinteger(L, EINVAL);
> +      return 3;
> +    case SYSPROF_ERRIO:
> +      return luaL_fileresult(L, 0, NULL);
> +    default:
> +      lua_assert(0);
> +      return 0;
> +  }
> +}
> +
> +/* local res, err, errno = sysprof.start(options) */
> +LJLIB_CF(misc_sysprof_start)
> +{
> +  int status = SYSPROF_SUCCESS;
> +
> +  struct luam_sysprof_options opt = {};
> +  struct profile_ctx *ctx = NULL;
> +
> +  ctx = lj_mem_new(L, sizeof(*ctx));

We don't need this context for streaming events if profiled mode is
default. Also we don't need to open any files to write data.

| src/luajit -e 'misc.sysprof.start( {mode = "D", interval = 1, path = "/tmp/sysprof.bin"})'
| luajit: /home/burii/reviews/luajit/sysprof/src/lj_state.c:199: close_state: Assertion `g->gc.total == sizeof(GG_State)' failed.
| Aborted

> +  ctx->g = G(L);
> +  opt.ctx = ctx;
> +  opt.buf = ctx->buf;
> +  opt.len = STREAM_BUFFER_SIZE;
> +
> +  status = parse_options(L, &opt);
> +  if (LJ_UNLIKELY(status != PROFILE_SUCCESS)) {
> +    lj_mem_free(ctx->g, ctx, sizeof(*ctx));
> +    return sysprof_error(L, status);
> +  }
> +
> +  const struct luam_sysprof_config conf = {buffer_writer_default, on_stop_cb_default, NULL};

Nit: linewidth is more than 80 symbols. Also, I see nothing bad to
set config fields per entry.

Also, we can't use non async-signal-safe functions in writer like
fwrite.

> +  status = luaM_sysprof_configure(&conf);
> +  if (LJ_UNLIKELY(status != PROFILE_SUCCESS)) {

We need to call `on_stop` callback manually here.

| $ src/luajit -e 'misc.sysprof.start( {mode = "C", interval = 1 end, path = "/tmp/sysprof.bin"}) misc.sysprof.start( {mode = "C", interval = 1 end, path = "/tmp/sysprof.bin"})'
| luajit: /home/burii/reviews/luajit/sysprof/src/lj_state.c:199: close_state: Assertion `g->gc.total == sizeof(GG_State)' failed.
| Aborted

> +    return sysprof_error(L, status);

Nit: {} are excess.

> +  }
> +
> +  status = luaM_sysprof_start(L, &opt);
> +  if (LJ_UNLIKELY(status != PROFILE_SUCCESS)) {
> +    /* Allocated memory will be freed in on_stop callback. */
> +    return sysprof_error(L, status);

Nit: {} are excess.

> +  }
> +
> +  lua_pushboolean(L, 1);
> +  return 1;
> +}
> +
> +/* local res, err, errno = profile.sysprof_stop() */
> +LJLIB_CF(misc_sysprof_stop)
> +{
> +  int status = luaM_sysprof_stop(L);
> +  if (LJ_UNLIKELY(status != PROFILE_SUCCESS)) {

Nit: {} are excess.

> +    return sysprof_error(L, status);
> +  }
> +  lua_pushboolean(L, 1);
> +  return 1;
> +}
> +
> +/* local counters, err, errno = sysprof.report() */
> +LJLIB_CF(misc_sysprof_report)
> +{
> +  struct luam_sysprof_counters counters = {};
> +  GCtab *data_tab = NULL;
> +  GCtab *count_tab = NULL;
> +
> +  int status = luaM_sysprof_report(&counters);
> +  if (status != SYSPROF_SUCCESS) {

Nit: {} are excess.

> +    return sysprof_error(L, status);
> +  }
> +
> +  lua_createtable(L, 0, 3);
> +  data_tab = tabV(L->top - 1);
> +
> +  setnumfield(L, data_tab, "samples", counters.samples);
> +  setnumfield(L, data_tab, "overruns", counters.overruns);
> +
> +  lua_createtable(L, 0, LJ_VMST__MAX + 1);

May be it is better to use 1 level tab here. Wait for the second
reviewer opinion.

> +  count_tab = tabV(L->top - 1);
> +
> +  setnumfield(L, count_tab, "INTERP", counters.vmst_interp);
> +  setnumfield(L, count_tab, "LFUNC",  counters.vmst_lfunc);
> +  setnumfield(L, count_tab, "FFUNC",  counters.vmst_ffunc);
> +  setnumfield(L, count_tab, "CFUNC",  counters.vmst_cfunc);
> +  setnumfield(L, count_tab, "GC",     counters.vmst_gc);
> +  setnumfield(L, count_tab, "EXIT",   counters.vmst_exit);
> +  setnumfield(L, count_tab, "RECORD", counters.vmst_record);
> +  setnumfield(L, count_tab, "OPT",    counters.vmst_opt);
> +  setnumfield(L, count_tab, "ASM",    counters.vmst_asm);
> +  setnumfield(L, count_tab, "TRACE",  counters.vmst_trace);
> +
> +  lua_setfield(L, -2, "vmstate");
> +
> +  return 1;
> +}
> +
>  /* ----- misc.memprof module ---------------------------------------------- */
>  
>  #define LJLIB_MODULE_misc_memprof

Nit: Empty line after LJLIB_MODULE_misc_memprof is missed.

> @@ -237,7 +449,19 @@ LJLIB_CF(misc_memprof_stop)
>  
>  LUALIB_API int luaopen_misc(struct lua_State *L)
>  {
> +  int status = SYSPROF_SUCCESS;
> +  struct luam_sysprof_config config = {};
> +
> +  config.writer = buffer_writer_default;
> +  config.on_stop = on_stop_cb_default;
> +  status = luaM_sysprof_configure(&config);
> +  if (LJ_UNLIKELY(status != PROFILE_SUCCESS)) {
> +    return sysprof_error(L, status);
> +  }

Don't get this: Why do we need to configure sysprof when loading module,
if we configure it manually during start() call?

> +
>    LJ_LIB_REG(L, LUAM_MISCLIBNAME, misc);
>    LJ_LIB_REG(L, LUAM_MISCLIBNAME ".memprof", misc_memprof);
> +  LJ_LIB_REG(L, LUAM_MISCLIBNAME ".sysprof", misc_sysprof);
> +
>    return 1;
>  }
> diff --git a/test/tarantool-tests/misclib-sysprof-lapi.test.lua b/test/tarantool-tests/misclib-sysprof-lapi.test.lua
> new file mode 100644
> index 00000000..17d83d1b
> --- /dev/null
> +++ b/test/tarantool-tests/misclib-sysprof-lapi.test.lua
> @@ -0,0 +1,105 @@

We need to use the same skipcondition as for memprof here.

> +local tap = require("tap")
> +
> +local test = tap.test("misc-sysprof-lapi")
> +test:plan(14)
> +
> +jit.off()
> +jit.flush()
> +
> +local bufread = require "utils.bufread"
> +local symtab = require "utils.symtab"

Nit: please be consistent: use `require("modulename")`.

> +
> +local TMP_BINFILE = arg[0]:gsub(".+/([^/]+)%.test%.lua$", "%.%1.sysprofdata.tmp.bin")
> +local BAD_PATH = arg[0]:gsub(".+/([^/]+)%.test%.lua$", "%1/sysprofdata.tmp.bin")
> +
> +local function payload()
> +  local function fib(n)
> +    if n <= 1 then
> +      return n
> +    end
> +    return fib(n - 1) + fib(n - 2)
> +  end
> +  return fib(32)
> +end
> +
> +local function generate_output(file, opts)
> +  file = file or "sysprof.bin"

Please, remove default file name.

> +  local res, err = misc.sysprof.start(file, opts)
> +  assert(res, err)
> +
> +  payload()
> +
> +  res,err = misc.sysprof.stop()
> +  assert(res, err)
> +end

Nit: missed empty line.
Here and below.

> +--## GENERAL ###############################################################--

Nit: this mixing style looks confusing. Please use only `--`.
Here and below.

> +
> +--wrong profiling mode

Typo: s/--wrong/-- Wrong/
Typo: s/mode/mode./

> +local res, err, errno = misc.sysprof.start{ mode = "A" }
> +test:ok(res == nil and err:match("profiler misuse"))
> +test:ok(type(errno) == "number")
> +
> +--already running

Typo: s/--already running/-- Already running./

> +res, err = misc.sysprof.start{ mode = "D" }
> +assert(res, err)
> +
> +res, err, errno = misc.sysprof.start{ mode = "D" }
> +print(err)

Please remove this debugging output.

> +test:ok(res == nil and err:match("profiler misuse"))
> +test:ok(type(errno) == "number")
> +
> +res, err = misc.sysprof.stop()
> +assert(res, err)
> +
> +--not running

Typo: s/--not running/-- Not running./

> +res, err, errno = misc.sysprof.stop()
> +test:ok(res == nil and err:match("profiler is not running"))
> +test:ok(type(errno) == "number")
> +
> +--bad path

Typo: s/--bad path/-- Bad path./

> +res, err, errno = misc.sysprof.start(BAD_PATH, { mode = "C" })
> +test:ok(res == nil and err:match("No such file or directory"))
> +test:ok(type(errno) == "number")
> +
> +--bad interval

Typo: s/--bad interval/-- Bad interval./

> +res, err, errno = misc.sysprof.start{ mode = "C", interval = -1 }
> +test:ok(res == nil and err:match("profiler misuse"))
> +test:ok(type(errno) == "number")
> +--## DEFAULT MODE ##########################################################--
> +
> +res, err = pcall(generate_output, nil, { mode = "D", interval = 11 })
> +if res == nil then

`pcall()` returns either true when there is no error raised, either
false in case of an error.
I suppose we should remove TMP file in case of error here.

> +  error(err)
> +end
> +
> +local report = misc.sysprof.report()
> +
> +test:ok(report.samples > 0)  -- TODO: more accurate test?

The test looks OK to me. Maybe we can use a specific function to sleep
every N ms, where N is the interval value, so then the amount of sleep
calls should be equal to the amount of samples.

> +test:ok(report.vmstate.LFUNC > 0)
> +test:ok(report.vmstate.TRACE == 0)
> +
> +-- with very big interval

Typo: s/with/With/
Typo: s/interval/interval./

> +res, err = pcall(generate_output, nil, { mode = "D", interval = 1000 })
> +if res == nil then

`pcall()` returns either true when there is no error raised, either
false in case of an error.
I suppose we should remove TMP file in case of error here.

> +  error(err)
> +end
> +
> +report = misc.sysprof.report()
> +test:ok(report.samples == 0)
> +
> +--## CALL MODE #############################################################--
> +
> +res, err = pcall(
> +  generate_output, TMP_BINFILE, { mode = "C", interval = 11 }
> +)
> +if res == nil then

`pcall()` returns either true when there is no error raised, either
false in case of an error.
I suppose we should remove TMP file in case of error here.

> +  error(err)
> +end
> +
> +local reader = bufread.new(TMP_BINFILE)
> +symtab.parse(reader)
> +
> +os.remove(TMP_BINFILE)
> +
> +jit.on()
> +os.exit(test:check() and 0 or 1)

What about Leaf mode? I suppose, that we can check it works like for
Callgraph mode.

> -- 
> 2.33.0
> 

-- 
Best regards,
Sergey Kaplun

  reply	other threads:[~2021-10-05 15:37 UTC|newest]

Thread overview: 20+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-09-08 17:50 [Tarantool-patches] [PATCH luajit 0/7] misc: introduce sysprof Maxim Kokryashkin via Tarantool-patches
2021-09-08 17:50 ` [Tarantool-patches] [PATCH luajit 1/7] core: save current frame top to lj_obj Maxim Kokryashkin via Tarantool-patches
2021-09-20 17:21   ` Sergey Kaplun via Tarantool-patches
2021-09-08 17:50 ` [Tarantool-patches] [PATCH luajit 2/7] core: separate the profiling timer from lj_profile Maxim Kokryashkin via Tarantool-patches
2021-09-21 11:13   ` Sergey Kaplun via Tarantool-patches
2021-09-23 11:37     ` Mikhail Shishatskiy via Tarantool-patches
2021-09-08 17:50 ` [Tarantool-patches] [PATCH luajit 3/7] memprof: move symtab to a separate module Maxim Kokryashkin via Tarantool-patches
2021-09-22  7:51   ` Sergey Kaplun via Tarantool-patches
2021-09-22  8:14     ` Sergey Kaplun via Tarantool-patches
2021-09-23 14:51   ` [Tarantool-patches] [PATCH luajit v2] " Maxim Kokryashkin via Tarantool-patches
2021-09-08 17:50 ` [Tarantool-patches] [PATCH luajit 4/7] core: introduce lua and platform profiler Maxim Kokryashkin via Tarantool-patches
2021-09-29  6:53   ` Sergey Kaplun via Tarantool-patches
2021-09-08 17:50 ` [Tarantool-patches] [PATCH luajit 5/7] memprof: add profile common section Maxim Kokryashkin via Tarantool-patches
2021-10-05 10:48   ` Sergey Kaplun via Tarantool-patches
2021-10-06 19:15     ` [Tarantool-patches] [PATCH luajit v2] " Maxim Kokryashkin via Tarantool-patches
2021-09-08 17:50 ` [Tarantool-patches] [PATCH luajit 6/7] sysprof: introduce Lua API Maxim Kokryashkin via Tarantool-patches
2021-10-05 15:36   ` Sergey Kaplun via Tarantool-patches [this message]
2021-09-08 17:50 ` [Tarantool-patches] [PATCH luajit 7/7] tools: introduce parsers for sysprof Maxim Kokryashkin via Tarantool-patches
2021-10-07 11:28   ` Sergey Kaplun via Tarantool-patches
2022-04-07 12:15 ` [Tarantool-patches] [PATCH luajit 0/7] misc: introduce sysprof Sergey Kaplun via Tarantool-patches

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=YVxw5keiLEwsCWS+@root \
    --to=tarantool-patches@dev.tarantool.org \
    --cc=max.kokryashkin@gmail.com \
    --cc=skaplun@tarantool.org \
    --subject='Re: [Tarantool-patches] [PATCH luajit 6/7] sysprof: introduce Lua API' \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox