From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from [87.239.111.99] (localhost [127.0.0.1]) by dev.tarantool.org (Postfix) with ESMTP id CE7D96EC57; Fri, 9 Jul 2021 14:05:19 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 dev.tarantool.org CE7D96EC57 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=tarantool.org; s=dev; t=1625828719; bh=QJwAFBjeGtw52bSPlrA6plhg9bZ2kGhLSnzD/+GyqR4=; h=To:Cc:Date:In-Reply-To:References:Subject:List-Id: List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe: From:Reply-To:From; b=rp6EAELPKRBr68Fo0iNVfyKwIa5xdMKDrBy3VjTJVjNGLpsnZa0ByWKU9mxG2RJ/c 0W0oLLDsdgJ6jvttvWazuy2Qgr+vNvnE1Oy3u4xDzrUtnlTxOeINh179AdALu34Ff0 /6iPF0fDmI04GoSo/rICurvXVvJBablMxvqdu1Xg= Received: from smtp33.i.mail.ru (smtp33.i.mail.ru [94.100.177.93]) (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 557526EC5C for ; Fri, 9 Jul 2021 14:04:17 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 dev.tarantool.org 557526EC5C Received: by smtp33.i.mail.ru with esmtpa (envelope-from ) id 1m1oIq-0008Ob-A5; Fri, 09 Jul 2021 14:04:16 +0300 To: gorcunov@tarantool.org, alyapunov@tarantool.org Cc: tarantool-patches@dev.tarantool.org, Egor Elchinov Date: Fri, 9 Jul 2021 14:03:51 +0300 Message-Id: X-Mailer: git-send-email 2.31.1 In-Reply-To: References: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-7564579A: 78E4E2B564C1792B X-77F55803: 4F1203BC0FB41BD954DFF1DC42D673FBE6FDDB4BD448567E7B50CED82977CC0D182A05F53808504045002EB3C8C6D195318995A22D16D0E5433A5639434CD7EFBD85B9CC454A1DE0 X-7FA49CB5: FF5795518A3D127A4AD6D5ED66289B5278DA827A17800CE7F16C4DE526EFCC04EA1F7E6F0F101C67BD4B6F7A4D31EC0BCC500DACC3FED6E28638F802B75D45FF8AA50765F79006371E92E38DA2D50EB18638F802B75D45FF36EB9D2243A4F8B5A6FCA7DBDB1FC311F39EFFDF887939037866D6147AF826D8A3F37CC4507D12C0F69C97B491277A5E117882F4460429724CE54428C33FAD305F5C1EE8F4F765FCAA867293B0326636D2E47CDBA5A96583BD4B6F7A4D31EC0BC014FD901B82EE079FA2833FD35BB23D27C277FBC8AE2E8BAA867293B0326636D2E47CDBA5A96583BA9C0B312567BB2376E601842F6C81A19E625A9149C048EE38D4DC57D478E688985B8ACC81218E19D8FC6C240DEA7642DBF02ECDB25306B2B78CF848AE20165D0A6AB1C7CE11FEE3CF7CD7A0D5AA5F259735652A29929C6CC4224003CC836476A09550CF41D4509EE2021AF6380DFAD1A18204E546F3947CB11811A4A51E3B096D1867E19FE1407959CC434672EE6371089D37D7C0E48F6C8AA50765F79006373AFABB501D265F08EFF80C71ABB335746BA297DBC24807EABDAD6C7F3747799A X-C1DE0DAB: C20DE7B7AB408E4181F030C43753B8186998911F362727C414F749A5E30D975C44FE9905A23AFB60C9F232CC2B42283C43723571FFB8F07F9C2B6934AE262D3EE7EAB7254005DCED7532B743992DF240BDC6A1CF3F042BAD6DF99611D93F60EF309DFB797F6729CB699F904B3F4130E343918A1A30D5E7FCCB5012B2E24CD356 X-C8649E89: 4E36BF7865823D7055A7F0CF078B5EC49A30900B95165D34128DEC38EFF4BE5AA60C1BDE3A25427DC1BDE290FCABD5A06DFEF81D907AEFCFE656CFB2108E91DD1D7E09C32AA3244C500025EF6C86CD05642656F1534D0ACE05AB220A9D022EBC927AC6DF5659F194 X-D57D3AED: 3ZO7eAau8CL7WIMRKs4sN3D3tLDjz0dLbV79QFUyzQ2Ujvy7cMT6pYYqY16iZVKkSc3dCLJ7zSJH7+u4VD18S7Vl4ZUrpaVfd2+vE6kuoey4m4VkSEu530nj6fImhcD4MUrOEAnl0W826KZ9Q+tr5ycPtXkTV4k65bRjmOUUP8cvGozZ33TWg5HZplvhhXbhDGzqmQDTd6OAevLeAnq3Ra9uf7zvY2zzsIhlcp/Y7m53TZgf2aB4JOg4gkr2biojbL9S8ysBdXhfGtq77ANurRuvWH503Z6/ X-Mailru-Sender: 11C2EC085EDE56FAC07928AF2646A769AA7B32CC0CAE825E318995A22D16D0E560497F7EF99AAC8F58570E9BDA2331C06F53C80213D1719C2C26F88BABE1618CA23003C376F5F1387402F9BA4338D657ED14614B50AE0675 X-Mras: Ok Subject: [Tarantool-patches] [PATCH v2 2/4] fiber: add option and PoC for Lua parent backtrace X-BeenThere: tarantool-patches@dev.tarantool.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: Tarantool development patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , From: Egor Elchinov via Tarantool-patches Reply-To: eelchinov@tarantool.org Errors-To: tarantool-patches-bounces@dev.tarantool.org Sender: "Tarantool-patches" From: Egor Elchinov For now Lua backtrace is fully collected at fiber creation moment and stored in a dynamically allocated buffer. This patch also adds option enabling to turn on and off the collection of backtraces of parent fibers when creating new fibers. Needed for: #4002 @TarantoolBot document Title: new option for fiber parent backtrace `fiber.parent_bt_enable()` and `fiber.parent_bt_disable()` turn on and off the ability to trace fiber creation stacks. If present this option may cause up to 40 per cent slowdown of fiber creation. By default parent backtrace is disabled. Note that fibers created while this option was disabled lack their parent backtraces in `fiber.info()` forever. --- src/lib/core/fiber.c | 29 ++- src/lib/core/fiber.h | 18 ++ src/lua/fiber.c | 200 ++++++++++++++++-- src/lua/fiber.h | 19 ++ .../gh-4002-fiber-creation-backtrace.result | 21 ++ .../gh-4002-fiber-creation-backtrace.test.lua | 7 + 6 files changed, 280 insertions(+), 14 deletions(-) diff --git a/src/lib/core/fiber.c b/src/lib/core/fiber.c index 924ff3c82..dd26e2f13 100644 --- a/src/lib/core/fiber.c +++ b/src/lib/core/fiber.c @@ -220,6 +220,10 @@ fiber_mprotect(void *addr, size_t len, int prot) static __thread bool fiber_top_enabled = false; #endif /* ENABLE_FIBER_TOP */ +#if ENABLE_BACKTRACE +static __thread bool fiber_parent_bt_enabled = false; +#endif /* ENABLE_BACKTRACE */ + /** * An action performed each time a context switch happens. * Used to count each fiber's processing time. @@ -1265,7 +1269,10 @@ fiber_new_ex(const char *name, const struct fiber_attr *fiber_attr, fiber->fid = cord->next_fid; fiber_set_name(fiber, name); #if ENABLE_BACKTRACE - backtrace_collect_ip(fiber->parent_bt_ip_buf, FIBER_PARENT_BT_MAX); + if (fiber_parent_bt_enabled) { + backtrace_collect_ip(fiber->parent_bt_ip_buf, + FIBER_PARENT_BT_MAX); + } #endif /* ENABLE_BACKTRACE */ register_fid(fiber); fiber->csw = 0; @@ -1414,6 +1421,26 @@ fiber_top_disable(void) } #endif /* ENABLE_FIBER_TOP */ +#if ENABLE_BACKTRACE +bool +fiber_parent_bt_is_enabled(void) +{ + return fiber_parent_bt_enabled; +} + +void +fiber_parent_bt_enable(void) +{ + fiber_parent_bt_enabled = true; +} + +void +fiber_parent_bt_disable(void) +{ + fiber_parent_bt_enabled = false; +} +#endif /* ENABLE_BACKTRACE */ + size_t box_region_used(void) { diff --git a/src/lib/core/fiber.h b/src/lib/core/fiber.h index beed58866..477b68e4c 100644 --- a/src/lib/core/fiber.h +++ b/src/lib/core/fiber.h @@ -528,6 +528,7 @@ struct txn; struct credentials; struct lua_State; struct ipc_wait_pad; +struct parent_bt_lua; struct fiber { coro_context ctx; @@ -634,6 +635,12 @@ struct fiber { * Optional fiber.storage Lua reference. */ int ref; +#if ENABLE_BACKTRACE + /** + * Lua parent backtrace (may be NULL) + */ + struct parent_bt_lua *parent_bt; +#endif /* ENABLE_BACKTRACE */ } lua; /** * Iproto sync. @@ -871,6 +878,17 @@ void fiber_top_disable(void); #endif /* ENABLE_FIBER_TOP */ +#if ENABLE_BACKTRACE +bool +fiber_parent_bt_is_enabled(void); + +void +fiber_parent_bt_enable(void); + +void +fiber_parent_bt_disable(void); +#endif /* ENABLE_BACKTRACE */ + /** Useful for C unit tests */ static inline int fiber_c_invoke(fiber_func f, va_list ap) diff --git a/src/lua/fiber.c b/src/lua/fiber.c index 7b21361d4..8e96059b4 100644 --- a/src/lua/fiber.c +++ b/src/lua/fiber.c @@ -193,6 +193,18 @@ struct lua_fiber_tb_ctx { int tb_frame; }; +/** + * Lua parent traceback context. + */ +struct lua_parent_tb_ctx { + /* Lua stack to push values. */ + struct lua_State *L; + /* Lua parent backtrace. */ + struct parent_bt_lua *bt; + /* Count of traced frames (both C and Lua). */ + int tb_frame; +}; + #ifdef ENABLE_BACKTRACE static void dump_lua_frame(struct lua_State *L, lua_Debug *ar, int tb_frame) @@ -209,6 +221,22 @@ dump_lua_frame(struct lua_State *L, lua_Debug *ar, int tb_frame) lua_settable(L, -3); } +static void +dump_parent_lua_frame(struct lua_State *L, const char *name, const char *src, + int currentline, int tb_frame) +{ + char buf[512]; + snprintf(buf, sizeof(buf), "%s in %s at line %i", + name != NULL ? name : "(unnamed)", + src, currentline); + lua_pushnumber(L, tb_frame); + lua_newtable(L); + lua_pushstring(L, "L"); + lua_pushstring(L, buf); + lua_settable(L, -3); + lua_settable(L, -3); +} + static int fiber_backtrace_cb(int frameno, void *frameret, const char *func, size_t offset, void *cb_ctx) { @@ -260,7 +288,115 @@ fiber_backtrace_cb(int frameno, void *frameret, const char *func, size_t offset, lua_settable(L, -3); return 0; } -#endif + +static int +fiber_parent_backtrace_cb(int frameno, void *frameret, const char *func, + size_t offset, void *cb_ctx) +{ + int lua_frame = 0; + struct lua_parent_tb_ctx *tb_ctx = (struct lua_parent_tb_ctx *)cb_ctx; + struct parent_bt_lua *bt = tb_ctx->bt; + struct lua_State *L = tb_ctx->L; + /* + * There is impossible to get func == NULL until + * https://github.com/tarantool/tarantool/issues/5326 + * will not resolved, but is possible afterwards. + */ + if (bt != NULL && func != NULL && strstr(func, "lj_BC_FUNCC") == func) { + /* We are in the LUA vm. */ + while (lua_frame < bt->cnt) { + tb_ctx->tb_frame++; + dump_parent_lua_frame(L, bt->names[lua_frame], + bt->sources[lua_frame], + bt->lines[lua_frame], + tb_ctx->tb_frame); + lua_frame++; + } + } + char buf[512]; + int l = snprintf(buf, sizeof(buf), "#%-2d %p in ", frameno, frameret); + if (func) + snprintf(buf + l, sizeof(buf) - l, "%s+%zu", func, offset); + else + snprintf(buf + l, sizeof(buf) - l, "?"); + tb_ctx->tb_frame++; + lua_pushnumber(L, tb_ctx->tb_frame); + lua_newtable(L); + lua_pushstring(L, "C"); + lua_pushstring(L, buf); + lua_settable(L, -3); + lua_settable(L, -3); + return 0; +} + +static int +fiber_parent_bt_init(struct fiber *f, struct lua_State *L) +{ + int lua_frame = 0, tb_frame = 0; + lua_Debug ar; + struct parent_bt_lua *bt = NULL; + + bt = (struct parent_bt_lua *)malloc(sizeof(*bt)); + if (bt == NULL){ + diag_set(OutOfMemory, sizeof(*bt), "malloc", "bt"); + return 1; + } + + while (tb_frame < PARENT_BT_LUA_LEN_MAX && + lua_getstack(L, lua_frame, &ar) > 0) { + /* Skip all following C-frames. */ + lua_getinfo(L, "Sln", &ar); + if (*ar.what != 'C') + break; + if (ar.name != NULL) { + /* Dump frame if it is a C built-in call. */ + bt->lines[tb_frame] = ar.currentline; + memset(bt->names[tb_frame], 0, + PARENT_BT_LUA_NAME_MAX); + strncpy(bt->names[tb_frame], ar.name, + PARENT_BT_LUA_NAME_MAX - 1); + memset(bt->sources[tb_frame], 0, + PARENT_BT_LUA_NAME_MAX); + strncpy(bt->sources[tb_frame], ar.source, + PARENT_BT_LUA_NAME_MAX - 1); + tb_frame++; + } + lua_frame++; + } + while (tb_frame < PARENT_BT_LUA_LEN_MAX && + lua_getstack(L, lua_frame, &ar) > 0) { + /* Trace Lua frame. */ + lua_getinfo(L, "Sln", &ar); + if (*ar.what == 'C') + break; + bt->lines[tb_frame] = ar.currentline; + memset(bt->names[tb_frame], 0, PARENT_BT_LUA_NAME_MAX); + if (ar.name != NULL) { + strncpy(bt->names[tb_frame], ar.name, + PARENT_BT_LUA_NAME_MAX - 1); + } else { + strncpy(bt->names[tb_frame], "(unnamed)", + PARENT_BT_LUA_NAME_MAX - 1); + } + memset(bt->sources[tb_frame], 0, PARENT_BT_LUA_NAME_MAX); + strncpy(bt->sources[tb_frame], ar.source, + PARENT_BT_LUA_NAME_MAX - 1); + tb_frame++; + lua_frame++; + } + + bt->cnt = tb_frame; + f->storage.lua.parent_bt = bt; + + return 0; +} + +static void +fiber_parent_bt_free(struct fiber *f) +{ + free(f->storage.lua.parent_bt); +} +#endif /* ENABLE_BACKTRACE */ static int lbox_fiber_statof_map(struct fiber *f, void *cb_ctx, bool backtrace) @@ -299,6 +435,7 @@ lbox_fiber_statof_map(struct fiber *f, void *cb_ctx, bool backtrace) if (backtrace) { #ifdef ENABLE_BACKTRACE struct lua_fiber_tb_ctx tb_ctx; + struct lua_parent_tb_ctx parent_tb_ctx; tb_ctx.L = L; tb_ctx.R = f->storage.lua.stack; tb_ctx.lua_frame = 0; @@ -309,15 +446,18 @@ lbox_fiber_statof_map(struct fiber *f, void *cb_ctx, bool backtrace) f != fiber() ? &f->ctx : NULL, &tb_ctx); lua_settable(L, -3); - tb_ctx.lua_frame = 0; - tb_ctx.tb_frame = 0; - tb_ctx.R = NULL; - lua_pushstring(L, "backtrace_parent"); - lua_newtable(L); - backtrace_foreach_ip(fiber_backtrace_cb, - f->parent_bt_ip_buf, - FIBER_PARENT_BT_MAX, &tb_ctx); - lua_settable(L, -3); + if (fiber_parent_bt_is_enabled()) { + parent_tb_ctx.L = L; + parent_tb_ctx.bt = f->storage.lua.parent_bt; + parent_tb_ctx.tb_frame = 0; + lua_pushstring(L, "backtrace_parent"); + lua_newtable(L); + backtrace_foreach_ip(fiber_parent_backtrace_cb, + f->parent_bt_ip_buf, + FIBER_PARENT_BT_MAX, + &parent_tb_ctx); + lua_settable(L, -3); + } #endif /* ENABLE_BACKTRACE */ } return 0; @@ -441,6 +581,22 @@ lbox_do_backtrace(struct lua_State *L) } return true; } + +static int +lbox_fiber_parent_bt_enable(struct lua_State *L) +{ + (void) L; + fiber_parent_bt_enable(); + return 0; +} + +static int +lbox_fiber_parent_bt_disable(struct lua_State *L) +{ + (void) L; + fiber_parent_bt_disable(); + return 0; +} #endif /* ENABLE_BACKTRACE */ /** @@ -481,10 +637,14 @@ lua_fiber_run_f(MAYBE_UNUSED va_list ap) * We can unref child stack here, * otherwise we have to unref child stack in join */ - if (f->flags & FIBER_IS_JOINABLE) + if (f->flags & FIBER_IS_JOINABLE) { lua_pushinteger(L, coro_ref); - else + } else { +#ifdef ENABLE_BACKTRACE + fiber_parent_bt_free(f); +#endif luaL_unref(L, LUA_REGISTRYINDEX, coro_ref); + } return result; } @@ -506,6 +666,12 @@ fiber_create(struct lua_State *L) luaT_error(L); } +#ifdef ENABLE_BACKTRACE + // TODO: error handling + if (fiber_parent_bt_is_enabled()) + fiber_parent_bt_init(f, L); +#endif + /* Move the arguments to the new coro */ lua_xmove(L, child_L, lua_gettop(L)); /* XXX: 'fiber' is leaked if this throws a Lua error. */ @@ -886,8 +1052,12 @@ lbox_fiber_join(struct lua_State *L) lua_xmove(child_L, L, num_ret); } } - if (child_L != NULL) + if (child_L != NULL) { +#ifdef ENABLE_BACKTRACE + fiber_parent_bt_free(fiber); +#endif luaL_unref(L, LUA_REGISTRYINDEX, coro_ref); + } return num_ret + 1; } @@ -941,6 +1111,10 @@ static const struct luaL_Reg fiberlib[] = { {"top_enable", lbox_fiber_top_enable}, {"top_disable", lbox_fiber_top_disable}, #endif /* ENABLE_FIBER_TOP */ +#if ENABLE_BACKTRACE + {"parent_bt_enable", lbox_fiber_parent_bt_enable}, + {"parent_bt_disable", lbox_fiber_parent_bt_disable}, +#endif /* ENABLE_BACKTRACE */ {"sleep", lbox_fiber_sleep}, {"yield", lbox_fiber_yield}, {"self", lbox_fiber_self}, diff --git a/src/lua/fiber.h b/src/lua/fiber.h index e29898706..450840fb0 100644 --- a/src/lua/fiber.h +++ b/src/lua/fiber.h @@ -36,6 +36,25 @@ extern "C" { struct lua_State; +/** + * Maximal name length (including '\0') + * and backtrace length. + */ +enum { + PARENT_BT_LUA_NAME_MAX = 64, + PARENT_BT_LUA_LEN_MAX = 8 +}; + +/** + * Stores lua parent backtrace for fiber. + */ +struct parent_bt_lua { + int cnt; + char names[PARENT_BT_LUA_LEN_MAX][PARENT_BT_LUA_NAME_MAX]; + char sources[PARENT_BT_LUA_LEN_MAX][PARENT_BT_LUA_NAME_MAX]; + int lines[PARENT_BT_LUA_LEN_MAX]; +}; + /** * Initialize box.fiber system */ diff --git a/test/app/gh-4002-fiber-creation-backtrace.result b/test/app/gh-4002-fiber-creation-backtrace.result index 4934b82d6..6a075911d 100644 --- a/test/app/gh-4002-fiber-creation-backtrace.result +++ b/test/app/gh-4002-fiber-creation-backtrace.result @@ -47,6 +47,17 @@ baz = function(n) bar(n) end | --- | ... +baz(10) + | --- + | ... +assert(parent_stack_len == -1) + | --- + | - true + | ... + +if stack_len ~= -1 then fiber:parent_bt_enable() end + | --- + | ... baz(10) | --- | ... @@ -55,3 +66,13 @@ assert(parent_stack_len > 0 or stack_len == -1) | - true | ... +if stack_len ~= -1 then fiber:parent_bt_disable() end + | --- + | ... +baz(10) + | --- + | ... +assert(parent_stack_len == -1) + | --- + | - true + | ... diff --git a/test/app/gh-4002-fiber-creation-backtrace.test.lua b/test/app/gh-4002-fiber-creation-backtrace.test.lua index 24d41a860..79a516860 100644 --- a/test/app/gh-4002-fiber-creation-backtrace.test.lua +++ b/test/app/gh-4002-fiber-creation-backtrace.test.lua @@ -22,6 +22,13 @@ local bar,baz bar = function(n) if n ~= 0 then baz(n-1) else fiber.create(foo) end end baz = function(n) bar(n) end +baz(10) +assert(parent_stack_len == -1) + +if stack_len ~= -1 then fiber:parent_bt_enable() end baz(10) assert(parent_stack_len > 0 or stack_len == -1) +if stack_len ~= -1 then fiber:parent_bt_disable() end +baz(10) +assert(parent_stack_len == -1) -- 2.31.1