From: Sergey Kaplun <skaplun@tarantool.org> To: Igor Munkin <imun@tarantool.org>, Sergey Ostanevich <sergos@tarantool.org> Cc: tarantool-patches@dev.tarantool.org Subject: [Tarantool-patches] [PATCH luajit v1 03/11] profile: introduce profiler writing module Date: Wed, 16 Dec 2020 22:13:38 +0300 [thread overview] Message-ID: <0adaa12ef5be0efc1d9c79ab746b8149c252d661.1608142899.git.skaplun@tarantool.org> (raw) In-Reply-To: <cover.1608142899.git.skaplun@tarantool.org> This patch introduces module for writing profile data. Its usage will be added at the next patches. It can be used for memory profiler or for signal-based cpu profiler. Part of tarantool/tarantool#5442 --- Custom memcpy function (see below) makes sense if this module will be used for cpu/sample profiler based on a signal-based timer. Else it can be easily redefined. src/Makefile | 5 +- src/Makefile.dep | 2 + src/profile/ljp_write.c | 195 ++++++++++++++++++++++++++++++++++++++++ src/profile/ljp_write.h | 84 +++++++++++++++++ 4 files changed, 284 insertions(+), 2 deletions(-) create mode 100644 src/profile/ljp_write.c create mode 100644 src/profile/ljp_write.h diff --git a/src/Makefile b/src/Makefile index be7ed95..4b1d937 100644 --- a/src/Makefile +++ b/src/Makefile @@ -469,6 +469,7 @@ DASM_FLAGS= $(DASM_XFLAGS) $(DASM_AFLAGS) DASM_DASC= vm_$(DASM_ARCH).dasc UTILS_O= utils/leb128.o +PROFILE_O= profile/ljp_write.o BUILDVM_O= host/buildvm.o host/buildvm_asm.o host/buildvm_peobj.o \ host/buildvm_lib.o host/buildvm_fold.o BUILDVM_T= host/buildvm @@ -499,7 +500,7 @@ LJCORE_O= lj_gc.o lj_err.o lj_char.o lj_bc.o lj_obj.o lj_buf.o \ lj_ctype.o lj_cdata.o lj_cconv.o lj_ccall.o lj_ccallback.o \ lj_carith.o lj_clib.o lj_cparse.o \ lj_lib.o lj_alloc.o lib_aux.o \ - $(LJLIB_O) lib_init.o $(UTILS_O) + $(LJLIB_O) lib_init.o $(UTILS_O) $(PROFILE_O) LJVMCORE_O= $(LJVM_O) $(LJCORE_O) LJVMCORE_DYNO= $(LJVMCORE_O:.o=_dyn.o) @@ -517,7 +518,7 @@ ALL_HDRGEN= lj_bcdef.h lj_ffdef.h lj_libdef.h lj_recdef.h lj_folddef.h \ host/buildvm_arch.h ALL_GEN= $(LJVM_S) $(ALL_HDRGEN) $(LIB_VMDEFP) WIN_RM= *.obj *.lib *.exp *.dll *.exe *.manifest *.pdb *.ilk -ALL_RM= $(ALL_T) $(ALL_GEN) *.o host/*.o utils/*.o $(WIN_RM) +ALL_RM= $(ALL_T) $(ALL_GEN) *.o host/*.o utils/*.o profile/*.o $(WIN_RM) ############################################################################## # Build mode handling. diff --git a/src/Makefile.dep b/src/Makefile.dep index cc75d03..7fdbfbe 100644 --- a/src/Makefile.dep +++ b/src/Makefile.dep @@ -248,4 +248,6 @@ host/buildvm_lib.o: host/buildvm_lib.c host/buildvm.h lj_def.h lua.h luaconf.h \ host/buildvm_peobj.o: host/buildvm_peobj.c host/buildvm.h lj_def.h lua.h \ luaconf.h lj_arch.h lj_bc.h lj_def.h lj_arch.h host/minilua.o: host/minilua.c +profile/ljp_write.o: profile/ljp_write.c profile/ljp_write.h utils/leb128.h \ + lj_def.h lua.h luaconf.h utils/leb128.o: utils/leb128.c diff --git a/src/profile/ljp_write.c b/src/profile/ljp_write.c new file mode 100644 index 0000000..de7202d --- /dev/null +++ b/src/profile/ljp_write.c @@ -0,0 +1,195 @@ +/* +** Low-level writer for LuaJIT Profiler. +** +** Major portions taken verbatim or adapted from the LuaVela. +** Copyright (C) 2015-2019 IPONWEB Ltd. +*/ + +#include <unistd.h> +#include <errno.h> + +#include "profile/ljp_write.h" +#include "utils/leb128.h" +#include "lj_def.h" + +/* +** memcpy from the standard C library is not guaranteed to be async-signal-safe. +** Common sense might tell us that it should be async-signal-safe (at least +** because it is unlikely that memcpy will allocate anything dynamically), +** but the standard is always the standard. So LuaJIT offers its own +** implementation of memcpy. Of course it will never be as fast as the system +** memcpy, but it will be guaranteed to always stay async-signal-safe. Feel +** free to remove the define below to fall-back to the system memcpy if the +** custom implementation becomes a botlleneck, but this is at your own risk. +** You are warned:) +*/ +#define USE_CUSTOM_LJ_MEMCPY + +#ifdef USE_CUSTOM_LJ_MEMCPY +/* +** Behaves exactly as memcpy from the standard C library with following caveats: +** - Guaranteed to be async-signal-safe. +** - Does *not* handle unaligned byte stores. +** - src cannot be declared as (const void *restrict) unless we start +** supporting C99 explicitly. Possible overlapping of dst in src is ignored. +*/ +#define COPY_BUFFER_TYPE uint64_t +#define COPY_BUFFER_SIZE sizeof(COPY_BUFFER_TYPE) + +static void *write_memcpy(void *dst, const void *src, size_t n) +{ + size_t loops; + size_t i; + uint8_t *dst_pos = (uint8_t *)dst; + const uint8_t *src_pos = (const uint8_t *)src; + + loops = n / COPY_BUFFER_SIZE; + for (i = 0; i < loops; i++) { + *(COPY_BUFFER_TYPE *)dst_pos = *(const COPY_BUFFER_TYPE *)src_pos; + dst_pos += COPY_BUFFER_SIZE; + src_pos += COPY_BUFFER_SIZE; + } + + loops = n % COPY_BUFFER_SIZE; + for (i = 0; i < loops; i++) { + *dst_pos = *src_pos; + dst_pos++; + src_pos++; + } + + return dst; +} +#else /* !USE_CUSTOM_LJ_MEMCPY */ +#define write_memcpy memcpy +#endif /* USE_CUSTOM_LJ_MEMCPY */ + +static LJ_AINLINE void write_set_flag(struct ljp_buffer *buf, uint8_t flag) +{ + buf->flags |= flag; +} + +static LJ_AINLINE void write_save_errno(struct ljp_buffer *buf) +{ + buf->saved_errno = errno; +} + +/* Wraps a write syscall ensuring all data have been written. */ +static void write_buffer_sys(struct ljp_buffer *buffer, const void **data, + size_t len) +{ + void *ctx = buffer->ctx; + size_t written; + + lua_assert(!ljp_write_test_flag(buffer, STREAM_STOP)); + + written = buffer->writer(data, len, ctx); + + if (LJ_UNLIKELY(written < len)) { + write_set_flag(buffer, STREAM_ERR_IO); + write_save_errno(buffer); + } + if (LJ_UNLIKELY(*data == NULL)) { + write_set_flag(buffer, STREAM_STOP); + write_save_errno(buffer); + } +} + +static LJ_AINLINE size_t write_bytes_buffered(const struct ljp_buffer *buf) +{ + return (size_t)(buf->pos - buf->buf); +} + +static LJ_AINLINE int write_buffer_has(const struct ljp_buffer *buf, size_t n) +{ + return (buf->size - write_bytes_buffered(buf)) >= n; +} + +void ljp_write_flush_buffer(struct ljp_buffer *buf) +{ + lua_assert(!ljp_write_test_flag(buf, STREAM_STOP)); + + write_buffer_sys(buf, (const void **)&buf->buf, write_bytes_buffered(buf)); + buf->pos = buf->buf; +} + +void ljp_write_init(struct ljp_buffer *buf, ljp_writer writer, void *ctx, + uint8_t *mem, size_t size) +{ + buf->ctx = ctx; + buf->writer = writer; + buf->buf = mem; + buf->pos = mem; + buf->size = size; + buf->flags = 0; + buf->saved_errno = 0; +} + +void ljp_write_terminate(struct ljp_buffer *buf) +{ + ljp_write_init(buf, NULL, NULL, NULL, 0); +} + +static LJ_AINLINE void write_reserve(struct ljp_buffer *buf, size_t n) +{ + if (LJ_UNLIKELY(!write_buffer_has(buf, n))) + ljp_write_flush_buffer(buf); +} + +/* Writes a byte to the output buffer. */ +void ljp_write_byte(struct ljp_buffer *buf, uint8_t b) +{ + if (LJ_UNLIKELY(ljp_write_test_flag(buf, STREAM_STOP))) + return; + write_reserve(buf, sizeof(b)); + *buf->pos++ = b; +} + +/* Writes an unsigned integer which is at most 64 bits long to the output. */ +void ljp_write_u64(struct ljp_buffer *buf, uint64_t n) +{ + if (LJ_UNLIKELY(ljp_write_test_flag(buf, STREAM_STOP))) + return; + write_reserve(buf, LEB128_U64_MAXSIZE); + buf->pos += (ptrdiff_t)write_uleb128(buf->pos, n); +} + +/* Writes n bytes from an arbitrary buffer src to the output. */ +static void write_buffer(struct ljp_buffer *buf, const void *src, size_t n) +{ + if (LJ_UNLIKELY(ljp_write_test_flag(buf, STREAM_STOP))) + return; + /* + ** Very unlikely: We are told to write a large buffer at once. + ** Buffer not belong to us so we must to pump data + ** through buffer. + */ + while (LJ_UNLIKELY(n > buf->size)) { + ljp_write_flush_buffer(buf); + write_memcpy(buf->pos, src, buf->size); + buf->pos += (ptrdiff_t)buf->size; + n -= buf->size; + } + + write_reserve(buf, n); + write_memcpy(buf->pos, src, n); + buf->pos += (ptrdiff_t)n; +} + +/* Writes a \0-terminated C string to the output buffer. */ +void ljp_write_string(struct ljp_buffer *buf, const char *s) +{ + const size_t l = strlen(s); + + ljp_write_u64(buf, (uint64_t)l); + write_buffer(buf, s, l); +} + +int ljp_write_test_flag(const struct ljp_buffer *buf, uint8_t flag) +{ + return buf->flags & flag; +} + +int ljp_write_errno(const struct ljp_buffer *buf) +{ + return buf->saved_errno; +} diff --git a/src/profile/ljp_write.h b/src/profile/ljp_write.h new file mode 100644 index 0000000..29c1669 --- /dev/null +++ b/src/profile/ljp_write.h @@ -0,0 +1,84 @@ +/* +** Low-level event streaming for LuaJIT Profiler. +** NB! Please note that all events may be streamed inside a signal handler. +** This means effectively that only async-signal-safe library functions and +** syscalls MUST be used for streaming. Check with `man 7 signal` when in +** doubt. +** Major portions taken verbatim or adapted from the LuaVela. +** Copyright (C) 2015-2019 IPONWEB Ltd. +*/ + +#ifndef _LJP_WRITE_H +#define _LJP_WRITE_H + +#include <stdint.h> + +/* +** Data format for strings: +** +** string := string-len string-payload +** string-len := <ULEB128> +** string-payload := <BYTE> {string-len} +** +** Note. +** For strings shorter than 128 bytes (most likely scenario in our case) +** we write the same amount of data (1-byte ULEB128 + actual payload) as we +** would have written with straightforward serialization (actual payload + \0), +** but make parsing easier. +*/ + +/* Stream errors. */ +#define STREAM_ERR_IO 0x1 +#define STREAM_STOP 0x2 + +typedef size_t (*ljp_writer)(const void **data, size_t len, void *opt); + +/* Write buffer for profilers. */ +struct ljp_buffer { + /* + ** Buffer writer which will called at buffer write. + ** Should return amount of written bytes on success or zero in case of error. + ** *data should contain new buffer of size greater or equal to len. + ** If *data == NULL stream stops. + */ + ljp_writer writer; + /* Context to writer function. */ + void *ctx; + /* Buffer size. */ + size_t size; + /* Saved errno in case of error. */ + int saved_errno; + /* Start of buffer. */ + uint8_t *buf; + /* Current position in buffer. */ + uint8_t *pos; + /* Internal flags. */ + volatile uint8_t flags; +}; + +/* Write string. */ +void ljp_write_string(struct ljp_buffer *buf, const char *s); + +/* Write single byte. */ +void ljp_write_byte(struct ljp_buffer *buf, uint8_t b); + +/* Write uint64_t in uleb128 format. */ +void ljp_write_u64(struct ljp_buffer *buf, uint64_t n); + +/* Immediatly flush buffer. */ +void ljp_write_flush_buffer(struct ljp_buffer *buf); + +/* Init buffer. */ +void ljp_write_init(struct ljp_buffer *buf, ljp_writer writer, void *ctx, + uint8_t *mem, size_t size); + +/* Check flags. */ +int ljp_write_test_flag(const struct ljp_buffer *buf, uint8_t flag); + +/* Return saved errno. */ +int ljp_write_errno(const struct ljp_buffer *buf); + +/* Set pointers to NULL and reset flags. */ +void ljp_write_terminate(struct ljp_buffer *buf); + +#endif -- 2.28.0
next prev parent reply other threads:[~2020-12-16 19:14 UTC|newest] Thread overview: 42+ messages / expand[flat|nested] mbox.gz Atom feed top 2020-12-16 19:13 [Tarantool-patches] [PATCH luajit v1 00/11] LuaJIT memory profiler Sergey Kaplun 2020-12-16 19:13 ` [Tarantool-patches] [PATCH luajit v1 01/11] build: add src dir in building Sergey Kaplun 2020-12-20 21:27 ` Igor Munkin 2020-12-23 18:20 ` Sergey Kaplun 2020-12-16 19:13 ` [Tarantool-patches] [PATCH luajit v1 02/11] utils: introduce leb128 reader and writer Sergey Kaplun 2020-12-20 22:44 ` Igor Munkin 2020-12-23 22:34 ` Sergey Kaplun 2020-12-24 9:11 ` Igor Munkin 2020-12-25 8:46 ` Sergey Kaplun 2020-12-23 16:50 ` Sergey Ostanevich 2020-12-23 22:36 ` Sergey Kaplun 2020-12-16 19:13 ` Sergey Kaplun [this message] 2020-12-21 9:24 ` [Tarantool-patches] [PATCH luajit v1 03/11] profile: introduce profiler writing module Igor Munkin 2020-12-24 6:46 ` Sergey Kaplun 2020-12-24 15:45 ` Sergey Ostanevich 2020-12-24 21:20 ` Sergey Kaplun 2020-12-25 9:37 ` Igor Munkin 2020-12-25 10:13 ` Sergey Kaplun 2020-12-16 19:13 ` [Tarantool-patches] [PATCH luajit v1 04/11] profile: introduce symtab write module Sergey Kaplun 2020-12-21 10:30 ` Igor Munkin 2020-12-24 7:00 ` Sergey Kaplun 2020-12-24 9:36 ` Igor Munkin 2020-12-25 8:45 ` Sergey Kaplun 2020-12-16 19:13 ` [Tarantool-patches] [PATCH luajit v1 05/11] vm: introduce LFUNC and FFUNC vmstates Sergey Kaplun 2020-12-25 11:07 ` Sergey Ostanevich 2020-12-25 11:23 ` Sergey Kaplun 2020-12-16 19:13 ` [Tarantool-patches] [PATCH luajit v1 06/11] core: introduce new mem_L field Sergey Kaplun 2020-12-16 19:13 ` [Tarantool-patches] [PATCH luajit v1 07/11] debug: move debug_frameline to public module API Sergey Kaplun 2020-12-20 22:46 ` Igor Munkin 2020-12-24 6:50 ` Sergey Kaplun 2020-12-16 19:13 ` [Tarantool-patches] [PATCH luajit v1 08/11] profile: introduce memory profiler Sergey Kaplun 2020-12-16 19:13 ` [Tarantool-patches] [PATCH luajit v1 09/11] misc: add Lua API for " Sergey Kaplun 2020-12-24 16:32 ` Sergey Ostanevich 2020-12-24 21:25 ` Sergey Kaplun 2020-12-16 19:13 ` [Tarantool-patches] [PATCH luajit v1 10/11] tools: introduce tools directory Sergey Kaplun 2020-12-20 22:46 ` Igor Munkin 2020-12-24 6:47 ` Sergey Kaplun 2020-12-16 19:13 ` [Tarantool-patches] [PATCH luajit v1 11/11] profile: introduce profile parser Sergey Kaplun 2020-12-24 23:09 ` Igor Munkin 2020-12-25 8:41 ` Sergey Kaplun 2020-12-21 10:43 ` [Tarantool-patches] [PATCH luajit v1 00/11] LuaJIT memory profiler Igor Munkin 2020-12-24 7:02 ` Sergey Kaplun
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=0adaa12ef5be0efc1d9c79ab746b8149c252d661.1608142899.git.skaplun@tarantool.org \ --to=skaplun@tarantool.org \ --cc=imun@tarantool.org \ --cc=sergos@tarantool.org \ --cc=tarantool-patches@dev.tarantool.org \ --subject='Re: [Tarantool-patches] [PATCH luajit v1 03/11] profile: introduce profiler writing module' \ /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