From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from smtp41.i.mail.ru (smtp41.i.mail.ru [94.100.177.101]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by dev.tarantool.org (Postfix) with ESMTPS id 2292B4765E2 for ; Fri, 25 Dec 2020 18:26:59 +0300 (MSK) From: Sergey Kaplun Date: Fri, 25 Dec 2020 18:26:04 +0300 Message-Id: In-Reply-To: References: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Subject: [Tarantool-patches] [PATCH luajit v2 2/7] core: introduce write buffer module List-Id: Tarantool development patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: Igor Munkin , Sergey Ostanevich Cc: tarantool-patches@dev.tarantool.org This patch introduces the standalone module for writing data to the file, socket or memory (and so on) via the special buffer. The module provides the API for buffer initial setup and its convenient usage. Part of tarantool/tarantool#5442 --- Changes in v2: - Removed custom memcpy. - lj_wbuf_addn() fills buffer to the end first and then flushes. - Changed assert in lj_wbuf_flush() to early return. src/Makefile | 2 +- src/Makefile.dep | 23 ++++---- src/lj_wbuf.c | 141 +++++++++++++++++++++++++++++++++++++++++++++++ src/lj_wbuf.h | 87 +++++++++++++++++++++++++++++ src/ljamalg.c | 1 + 5 files changed, 242 insertions(+), 12 deletions(-) create mode 100644 src/lj_wbuf.c create mode 100644 src/lj_wbuf.h diff --git a/src/Makefile b/src/Makefile index dc2ddb6..384b590 100644 --- a/src/Makefile +++ b/src/Makefile @@ -485,7 +485,7 @@ LJLIB_O= lib_base.o lib_math.o lib_bit.o lib_string.o lib_table.o \ lib_misc.o LJLIB_C= $(LJLIB_O:.o=.c) -LJCORE_O= lj_gc.o lj_err.o lj_char.o lj_bc.o lj_obj.o lj_buf.o \ +LJCORE_O= lj_gc.o lj_err.o lj_char.o lj_bc.o lj_obj.o lj_buf.o lj_wbuf.o \ lj_str.o lj_tab.o lj_func.o lj_udata.o lj_meta.o lj_debug.o \ lj_state.o lj_dispatch.o lj_vmevent.o lj_vmmath.o lj_strscan.o \ lj_strfmt.o lj_strfmt_num.o lj_api.o lj_mapi.o lj_profile.o \ diff --git a/src/Makefile.dep b/src/Makefile.dep index 75409bf..59ed450 100644 --- a/src/Makefile.dep +++ b/src/Makefile.dep @@ -211,28 +211,29 @@ lj_vmevent.o: lj_vmevent.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ lj_vm.h lj_vmevent.h lj_vmmath.o: lj_vmmath.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ lj_ir.h lj_vm.h +lj_wbuf.o: lj_wbuf.c lj_wbuf.h lj_def.h lua.h luaconf.h lj_utils.h ljamalg.o: ljamalg.c lua.h luaconf.h lauxlib.h lj_gc.c lj_obj.h lj_def.h \ lj_arch.h lj_gc.h lj_err.h lj_errmsg.h lj_buf.h lj_str.h lj_tab.h \ lj_func.h lj_udata.h lj_meta.h lj_state.h lj_frame.h lj_bc.h lj_ctype.h \ lj_cdata.h lj_trace.h lj_jit.h lj_ir.h lj_dispatch.h lj_traceerr.h \ lj_vm.h lj_err.c lj_debug.h lj_ff.h lj_ffdef.h lj_strfmt.h lj_char.c \ - lj_char.h lj_bc.c lj_bcdef.h lj_obj.c lj_buf.c lj_str.c lj_tab.c \ - lj_func.c lj_udata.c lj_meta.c lj_strscan.h lj_lib.h lj_debug.c \ - lj_state.c lj_lex.h lj_alloc.h luajit.h lj_dispatch.c lj_ccallback.h \ - lj_profile.h lj_vmevent.c lj_vmevent.h lj_vmmath.c lj_strscan.c \ - lj_strfmt.c lj_strfmt_num.c lj_api.c lj_mapi.c lmisclib.h lj_profile.c \ - lj_lex.c lualib.h lj_parse.h lj_parse.c lj_bcread.c lj_bcdump.h lj_bcwrite.c \ - lj_load.c lj_ctype.c lj_cdata.c lj_cconv.h lj_cconv.c lj_ccall.c lj_ccall.h \ - lj_ccallback.c lj_target.h lj_target_*.h lj_mcode.h lj_carith.c \ + lj_char.h lj_bc.c lj_bcdef.h lj_obj.c lj_buf.c lj_wbuf.c lj_wbuf.h lj_utils.h \ + lj_str.c lj_tab.c lj_func.c lj_udata.c lj_meta.c lj_strscan.h lj_lib.h \ + lj_debug.c lj_state.c lj_lex.h lj_alloc.h luajit.h lj_dispatch.c \ + lj_ccallback.h lj_profile.h lj_vmevent.c lj_vmevent.h lj_vmmath.c \ + lj_strscan.c lj_strfmt.c lj_strfmt_num.c lj_api.c lj_mapi.c lmisclib.h \ + lj_profile.c lj_lex.c lualib.h lj_parse.h lj_parse.c lj_bcread.c lj_bcdump.h \ + lj_bcwrite.c lj_load.c lj_ctype.c lj_cdata.c lj_cconv.h lj_cconv.c lj_ccall.c \ + lj_ccall.h lj_ccallback.c lj_target.h lj_target_*.h lj_mcode.h lj_carith.c \ lj_carith.h lj_clib.c lj_clib.h lj_cparse.c lj_cparse.h lj_lib.c lj_ir.c \ lj_ircall.h lj_iropt.h lj_opt_mem.c lj_opt_fold.c lj_folddef.h \ lj_opt_narrow.c lj_opt_dce.c lj_opt_loop.c lj_snap.h lj_opt_split.c \ lj_opt_sink.c lj_mcode.c lj_snap.c lj_record.c lj_record.h lj_ffrecord.h \ lj_crecord.c lj_crecord.h lj_ffrecord.c lj_recdef.h lj_asm.c lj_asm.h \ lj_emit_*.h lj_asm_*.h lj_trace.c lj_gdbjit.h lj_gdbjit.c lj_alloc.c \ - lj_utils_leb128.c lj_utils.h lib_aux.c lib_base.c lj_libdef.h lib_math.c \ - lib_string.c lib_table.c lib_io.c lib_os.c lib_package.c lib_debug.c \ - lib_bit.c lib_jit.c lib_ffi.c lib_misc.c lib_init.c + lj_utils_leb128.c lib_aux.c lib_base.c lj_libdef.h lib_math.c lib_string.c \ + lib_table.c lib_io.c lib_os.c lib_package.c lib_debug.c lib_bit.c lib_jit.c \ + lib_ffi.c lib_misc.c lib_init.c luajit.o: luajit.c lua.h luaconf.h lauxlib.h lualib.h luajit.h lj_arch.h host/buildvm.o: host/buildvm.c host/buildvm.h lj_def.h lua.h luaconf.h \ lj_arch.h lj_obj.h lj_def.h lj_arch.h lj_gc.h lj_obj.h lj_bc.h lj_ir.h \ diff --git a/src/lj_wbuf.c b/src/lj_wbuf.c new file mode 100644 index 0000000..8f090eb --- /dev/null +++ b/src/lj_wbuf.c @@ -0,0 +1,141 @@ +/* +** Low-level writer for LuaJIT. +** +** Major portions taken verbatim or adapted from the LuaVela. +** Copyright (C) 2015-2019 IPONWEB Ltd. +*/ + +#define lj_wbuf_c +#define LUA_CORE + +#include + +#include "lj_wbuf.h" +#include "lj_utils.h" + +static LJ_AINLINE void wbuf_set_flag(struct lj_wbuf *buf, uint8_t flag) +{ + buf->flags |= flag; +} + +static LJ_AINLINE void wbuf_save_errno(struct lj_wbuf *buf) +{ + buf->saved_errno = errno; +} + +static LJ_AINLINE size_t wbuf_len(const struct lj_wbuf *buf) +{ + return (size_t)(buf->pos - buf->buf); +} + +static LJ_AINLINE size_t wbuf_left(const struct lj_wbuf *buf) +{ + return buf->size - wbuf_len(buf); +} + +void lj_wbuf_init(struct lj_wbuf *buf, lj_wbuf_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 LJ_FASTCALL lj_wbuf_terminate(struct lj_wbuf *buf) +{ + lj_wbuf_init(buf, NULL, NULL, NULL, 0); +} + +static LJ_AINLINE void wbuf_reserve(struct lj_wbuf *buf, size_t n) +{ + lua_assert(n <= buf->size); + if (LJ_UNLIKELY(wbuf_left(buf) < n)) + lj_wbuf_flush(buf); +} + +/* Writes a byte to the output buffer. */ +void LJ_FASTCALL lj_wbuf_addbyte(struct lj_wbuf *buf, uint8_t b) +{ + if (LJ_UNLIKELY(lj_wbuf_test_flag(buf, STREAM_STOP))) + return; + wbuf_reserve(buf, sizeof(b)); + *buf->pos++ = b; +} + +/* Writes an unsigned integer which is at most 64 bits long to the output. */ +void LJ_FASTCALL lj_wbuf_addu64(struct lj_wbuf *buf, uint64_t n) +{ + if (LJ_UNLIKELY(lj_wbuf_test_flag(buf, STREAM_STOP))) + return; + wbuf_reserve(buf, LEB128_U64_MAXSIZE); + buf->pos += (ptrdiff_t)lj_utils_write_uleb128(buf->pos, n); +} + +/* Writes n bytes from an arbitrary buffer src to the buffer. */ +void lj_wbuf_addn(struct lj_wbuf *buf, const void *src, size_t n) +{ + if (LJ_UNLIKELY(lj_wbuf_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)) { + const size_t left = wbuf_left(buf); + memcpy(buf->pos, src, left); + buf->pos += (ptrdiff_t)left; + lj_wbuf_flush(buf); + src += (ptrdiff_t)left; + n -= left; + } + + wbuf_reserve(buf, n); + memcpy(buf->pos, src, n); + buf->pos += (ptrdiff_t)n; +} + +/* Writes a \0-terminated C string to the output buffer. */ +void LJ_FASTCALL lj_wbuf_addstring(struct lj_wbuf *buf, const char *s) +{ + const size_t l = strlen(s); + + /* Check that profiling is still active is made in the callee's scope. */ + lj_wbuf_addu64(buf, (uint64_t)l); + lj_wbuf_addn(buf, s, l); +} + +void LJ_FASTCALL lj_wbuf_flush(struct lj_wbuf *buf) +{ + const size_t len = wbuf_len(buf); + size_t written; + + if (LJ_UNLIKELY(lj_wbuf_test_flag(buf, STREAM_STOP))) + return; + + written = buf->writer((const void **)&buf->buf, len, buf->ctx); + + if (LJ_UNLIKELY(written < len)) { + wbuf_set_flag(buf, STREAM_ERR_IO); + wbuf_save_errno(buf); + } + if (LJ_UNLIKELY(buf->buf == NULL)) { + wbuf_set_flag(buf, STREAM_STOP); + wbuf_save_errno(buf); + } + buf->pos = buf->buf; +} + +int LJ_FASTCALL lj_wbuf_test_flag(const struct lj_wbuf *buf, uint8_t flag) +{ + return buf->flags & flag; +} + +int LJ_FASTCALL lj_wbuf_errno(const struct lj_wbuf *buf) +{ + return buf->saved_errno; +} diff --git a/src/lj_wbuf.h b/src/lj_wbuf.h new file mode 100644 index 0000000..58a109e --- /dev/null +++ b/src/lj_wbuf.h @@ -0,0 +1,87 @@ +/* +** Low-level event streaming for LuaJIT Profiler. +** +** XXX: Please note that all events may not be streamed inside a signal handler +** due to using default memcpy from glibc as not async-signal-safe function. +** +** Major portions taken verbatim or adapted from the LuaVela. +** Copyright (C) 2015-2019 IPONWEB Ltd. +*/ + +#ifndef _LJ_WBUF_H +#define _LJ_WBUF_H + +#include "lj_def.h" + +/* +** Data format for strings: +** +** string := string-len string-payload +** string-len := +** string-payload := {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 (*lj_wbuf_writer)(const void **data, size_t len, void *opt); + +/* Write buffer. */ +struct lj_wbuf { + /* + ** 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. + */ + lj_wbuf_writer writer; + /* Context for 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; +}; + +/* Init buffer. */ +void lj_wbuf_init(struct lj_wbuf *buf, lj_wbuf_writer writer, void *ctx, + uint8_t *mem, size_t size); + +/* Set pointers to NULL and reset flags and errno. */ +void LJ_FASTCALL lj_wbuf_terminate(struct lj_wbuf *buf); + +/* Write single byte to the buffer. */ +void LJ_FASTCALL lj_wbuf_addbyte(struct lj_wbuf *buf, uint8_t b); + +/* Write uint64_t in uleb128 format to the buffer. */ +void LJ_FASTCALL lj_wbuf_addu64(struct lj_wbuf *buf, uint64_t n); + +/* Writes n bytes from an arbitrary buffer src to the buffer. */ +void lj_wbuf_addn(struct lj_wbuf *buf, const void *src, size_t n); + +/* Write string to the buffer. */ +void LJ_FASTCALL lj_wbuf_addstring(struct lj_wbuf *buf, const char *s); + +/* Immediatly flush the buffer. */ +void LJ_FASTCALL lj_wbuf_flush(struct lj_wbuf *buf); + +/* Check flags. */ +int LJ_FASTCALL lj_wbuf_test_flag(const struct lj_wbuf *buf, uint8_t flag); + +/* Return saved errno. */ +int LJ_FASTCALL lj_wbuf_errno(const struct lj_wbuf *buf); + +#endif diff --git a/src/ljamalg.c b/src/ljamalg.c index 268b321..705e296 100644 --- a/src/ljamalg.c +++ b/src/ljamalg.c @@ -34,6 +34,7 @@ #include "lj_bc.c" #include "lj_obj.c" #include "lj_buf.c" +#include "lj_wbuf.c" #include "lj_str.c" #include "lj_tab.c" #include "lj_func.c" -- 2.28.0