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 9EB3D6EC55; Wed, 14 Jul 2021 14:14:03 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 dev.tarantool.org 9EB3D6EC55 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=tarantool.org; s=dev; t=1626261243; bh=Ibf7KKS44+ruSl7FWkcNCnyrvJdj5gFJ1GIx4jyEIVo=; 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=HAOa935aHHBQKSWc8knu6+1DbG2/O6xJ1GLbesXJIJ8woMRLMRUJU16UT07KtCoMn P0kcmRMwIg2Li8R3RaaznLJHIhMM0TSjDDrO+dVEdFvHh1cMqERaaB2cLC+HkmZgZT iw/RrL4/NcYHtEsjWQbC17nd2ViV1cIMvsVeUTfA= Received: from smtp59.i.mail.ru (smtp59.i.mail.ru [217.69.128.39]) (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 B58546EC55 for ; Wed, 14 Jul 2021 14:13:34 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 dev.tarantool.org B58546EC55 Received: by smtp59.i.mail.ru with esmtpa (envelope-from ) id 1m3cpZ-0001Jt-MB; Wed, 14 Jul 2021 14:13:34 +0300 To: gorcunov@tarantool.org, alyapunov@tarantool.org Cc: tarantool-patches@dev.tarantool.org, Egor Elchinov Date: Wed, 14 Jul 2021 14:12:49 +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: 646B95376F6C166E X-77F55803: 4F1203BC0FB41BD97BB0EF39AD2B33D54F26E6113A59F95A22EFF9DCA932A94B182A05F538085040655639C14D6F25359851EBBA8B2336D7661D1B5DDF669E7AD4E560FF3F7ADE8D X-7FA49CB5: FF5795518A3D127A4AD6D5ED66289B5278DA827A17800CE7837EFDF6F90DE77AEA1F7E6F0F101C67BD4B6F7A4D31EC0BCC500DACC3FED6E28638F802B75D45FF8AA50765F79006375DA0115FC9FAE6C68638F802B75D45FF36EB9D2243A4F8B5A6FCA7DBDB1FC311F39EFFDF887939037866D6147AF826D8D56E41B6018CFE3ED01E2E5BC0DECEF3117882F4460429724CE54428C33FAD305F5C1EE8F4F765FCE96A3A8AAADC8934A471835C12D1D9774AD6D5ED66289B52BA9C0B312567BB23117882F4460429728776938767073520CCD848CCB6FE560CE5D25F19253116ADD2E47CDBA5A96583BA9C0B312567BB2376E601842F6C81A19E625A9149C048EEFAD5A440E159F97D2882943172349060D8FC6C240DEA7642DBF02ECDB25306B2B78CF848AE20165D0A6AB1C7CE11FEE3CF7CD7A0D5AA5F2503F1AB874ED89028C4224003CC836476EA7A3FFF5B025636E2021AF6380DFAD1A18204E546F3947CB11811A4A51E3B096D1867E19FE1407959CC434672EE6371089D37D7C0E48F6C8AA50765F790063717E6A56809D3D6D1EFF80C71ABB335746BA297DBC24807EABDAD6C7F3747799A X-C1DE0DAB: C20DE7B7AB408E4181F030C43753B8186998911F362727C414F749A5E30D975CE68746B1F2AB10C6C93D2F2FA857FE1DBEDBF124259019899C2B6934AE262D3EE7EAB7254005DCED7532B743992DF240BDC6A1CF3F042BAD6DF99611D93F60EF309DFB797F6729CB699F904B3F4130E343918A1A30D5E7FCCB5012B2E24CD356 X-C8649E89: 4E36BF7865823D7055A7F0CF078B5EC49A30900B95165D34B3611847B8BC2D0B080106157CFFAC04A52265E1EAFF4CDEA9B34391076FE8BB1A8CB34D624B5A661D7E09C32AA3244C8779943B1059F8DF8D77F6FBD268FC7F795D98D676DD64D0927AC6DF5659F194 X-D57D3AED: 3ZO7eAau8CL7WIMRKs4sN3D3tLDjz0dLbV79QFUyzQ2Ujvy7cMT6pYYqY16iZVKkSc3dCLJ7zSJH7+u4VD18S7Vl4ZUrpaVfd2+vE6kuoey4m4VkSEu530nj6fImhcD4MUrOEAnl0W826KZ9Q+tr5ycPtXkTV4k65bRjmOUUP8cvGozZ33TWg5HZplvhhXbhDGzqmQDTd6OAevLeAnq3Ra9uf7zvY2zzsIhlcp/Y7m53TZgf2aB4JOg4gkr2biojDdSFIg49M1Rp92IOQ5RbcA== X-Mailru-Sender: EFA0F3A8419EF2166B053C73E674FFD5719BA972463E31FFE7AAEF81D3CD05DEE2527C969975515C67F54F2D6EFFC80BC77752E0C033A69E17841C44D9B5D58765F2F89A5AFDB6F16C18EFA0BB12DBB0 X-Mras: Ok Subject: [Tarantool-patches] [PATCH v3 1/4] fiber: add PoC for fiber creation 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 fiber creation backtrace is stored in the separate subtable of fiber.info called backtrace_parent for convenience. Lua stacks of fiber creation aren't preserved in backtrace yet because of need to somehow handle parent Lua state inside the child fiber for this sake. Backtrace caching and demangling aren't present yet too as this is a proof-of-concept implementation. Due to the limited libunwind API under osx for now this patch uses backtrace() from and dladdr() from instead of absent libunwind routines. Custom backtrace() function was renamed to backtrace_to_buf() because of the name conflict with backtrace() routine from . Needed for: #4002 --- src/lib/core/backtrace.cc | 99 ++++++++++++++++++- src/lib/core/backtrace.h | 10 +- src/lib/core/crash.c | 2 +- src/lib/core/fiber.c | 8 ++ src/lib/core/fiber.h | 16 +++ src/lua/fiber.c | 10 ++ .../gh-4002-fiber-creation-backtrace.result | 57 +++++++++++ .../gh-4002-fiber-creation-backtrace.test.lua | 27 +++++ 8 files changed, 225 insertions(+), 4 deletions(-) create mode 100644 test/app/gh-4002-fiber-creation-backtrace.result create mode 100644 test/app/gh-4002-fiber-creation-backtrace.test.lua diff --git a/src/lib/core/backtrace.cc b/src/lib/core/backtrace.cc index b4048089f..ef8a70f1b 100644 --- a/src/lib/core/backtrace.cc +++ b/src/lib/core/backtrace.cc @@ -46,6 +46,11 @@ #ifdef ENABLE_BACKTRACE #include +#ifdef TARGET_OS_DARWIN +#include +#include +#endif + #include "small/region.h" #include "small/static.h" /* @@ -131,7 +136,7 @@ error: } char * -backtrace(char *start, size_t size) +backtrace_to_buf(char *start, size_t size) { int frame_no = 0; unw_word_t sp = 0, old_sp = 0, ip, offset; @@ -432,11 +437,101 @@ out: free(demangle_buf); } +/** + * Collect up to `limit' IP register values + * for frames of the current stack into `ip_buf'. + * Must be by far faster than usual backtrace according to the + * libunwind doc for unw_backtrace(). + */ +void NOINLINE +backtrace_collect_ip(void **ip_buf, int limit) +{ + memset(ip_buf, 0, limit * sizeof(*ip_buf)); +#ifndef TARGET_OS_DARWIN + unw_backtrace(ip_buf, limit); +#else + backtrace(ip_buf, limit); +#endif +} + +/** + * Call `cb' callback for not more than + * first `limit' frames present in the `ip_buf'. + * + * The implementation uses poorly documented `get_proc_name' callback + * from the `unw_accessors_t' to get procedure names via `ip_buf' values. + * Although `get_proc_name' is present on most architectures, it's an optional + * field, so procedure name is allowed to be absent (NULL) in `cb' call. + * + * TODO: to add cache and demangling support + */ +void +backtrace_foreach_ip(backtrace_cb cb, void **ip_buf, int limit, + void *cb_ctx) +{ +#ifndef TARGET_OS_DARWIN + char proc_name[BACKTRACE_NAME_MAX]; + unw_word_t ip = 0, offset = 0; + unw_proc_info_t pi; + int frame_no, ret = 0; + char *proc = NULL; + + unw_accessors_t *acc = unw_get_accessors(unw_local_addr_space); + + /* + * RIPs collecting comes from inside a helper routine + * so we skip the collector function address itself thus + * start fetching functions with frame number = 1. + */ + for (frame_no = 1; frame_no < limit && ip_buf[frame_no] != NULL; + frame_no++) { + ip = (unw_word_t)ip_buf[frame_no]; + + if (acc->get_proc_name == NULL) { + ret = unw_get_proc_info_by_ip(unw_local_addr_space, + ip, &pi, NULL); + offset = ip - pi.start_ip; + } else { + ret = acc->get_proc_name(unw_local_addr_space, ip, + proc_name, sizeof(proc_name), + &offset, NULL); + proc = proc_name; + } + if (ret != 0 || cb(frame_no - 1, (void *)ip, proc, + (size_t)offset, cb_ctx) != 0) + break; + } + + if (ret != 0) + say_debug("unwinding error: %s", unw_strerror(ret)); +#else + int frame_no, ret = 1; + void *ip = NULL; + size_t offset = 0; + Dl_info dli; + + for (frame_no = 1; frame_no < limit && ip_buf[frame_no] != NULL; + ++frame_no) { + ip = ip_buf[frame_no]; + ret = dladdr(ip, &dli); + if (ret == 0) + break; + offset = (char *)ip - (char *)dli.dli_saddr; + + if (cb(frame_no - 1, ip, dli.dli_sname, offset, cb_ctx) != 0) + break; + } + + if (ret == 0) + say_debug("unwinding error: %i", ret); +#endif +} + void print_backtrace(void) { char *start = (char *)static_alloc(SMALL_STATIC_SIZE); - fdprintf(STDERR_FILENO, "%s", backtrace(start, SMALL_STATIC_SIZE)); + fdprintf(STDERR_FILENO, "%s", backtrace_to_buf(start, SMALL_STATIC_SIZE)); } #endif /* ENABLE_BACKTRACE */ diff --git a/src/lib/core/backtrace.h b/src/lib/core/backtrace.h index e0ae56be4..4552c14e1 100644 --- a/src/lib/core/backtrace.h +++ b/src/lib/core/backtrace.h @@ -31,6 +31,7 @@ * SUCH DAMAGE. */ #include "trivia/config.h" +#include "trivia/util.h" #include #if defined(__cplusplus) @@ -41,7 +42,7 @@ extern "C" { #include char * -backtrace(char *start, size_t size); +backtrace_to_buf(char *start, size_t size); void print_backtrace(void); @@ -55,6 +56,13 @@ backtrace_foreach(backtrace_cb cb, coro_context *coro_ctx, void *cb_ctx); void backtrace_proc_cache_clear(void); +void NOINLINE +backtrace_collect_ip(void **ip_buf, int limit); + +void +backtrace_foreach_ip(backtrace_cb cb, void **ip_buf, int limit, + void *cb_ctx); + #endif /* ENABLE_BACKTRACE */ #if defined(__cplusplus) diff --git a/src/lib/core/crash.c b/src/lib/core/crash.c index abb7837e6..d1990d68b 100644 --- a/src/lib/core/crash.c +++ b/src/lib/core/crash.c @@ -213,7 +213,7 @@ crash_collect(int signo, siginfo_t *siginfo, void *ucontext) #ifdef ENABLE_BACKTRACE char *start = cinfo->backtrace_buf; - backtrace(start, sizeof(cinfo->backtrace_buf)); + backtrace_to_buf(start, sizeof(cinfo->backtrace_buf)); #endif #ifdef HAS_GREG diff --git a/src/lib/core/fiber.c b/src/lib/core/fiber.c index 759c7da6a..924ff3c82 100644 --- a/src/lib/core/fiber.c +++ b/src/lib/core/fiber.c @@ -45,6 +45,11 @@ extern void cord_on_yield(void); +#if ENABLE_BACKTRACE +#include "backtrace.h" /* fast_trace */ + +#endif /* ENABLE_BACKTRACE */ + #if ENABLE_FIBER_TOP #include /* __rdtscp() */ @@ -1259,6 +1264,9 @@ fiber_new_ex(const char *name, const struct fiber_attr *fiber_attr, fiber->f = f; 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); +#endif /* ENABLE_BACKTRACE */ register_fid(fiber); fiber->csw = 0; diff --git a/src/lib/core/fiber.h b/src/lib/core/fiber.h index 8f4e14796..beed58866 100644 --- a/src/lib/core/fiber.h +++ b/src/lib/core/fiber.h @@ -111,6 +111,18 @@ struct cpu_stat { #endif /* ENABLE_FIBER_TOP */ +#if ENABLE_BACKTRACE + +enum { + /** + * Maximum entries count to grab + * from the fiber creation backtrace. + */ + FIBER_PARENT_BT_MAX = 8 +}; + +#endif /* ENABLE_BACKTRACE */ + enum { /** Both limits include terminating 0. */ FIBER_NAME_INLINE = 40, @@ -640,6 +652,10 @@ struct fiber { */ char *name; char inline_name[FIBER_NAME_INLINE]; +#if ENABLE_BACKTRACE + /** Fiber creation backtrace chunk. */ + void *parent_bt_ip_buf[FIBER_PARENT_BT_MAX]; +#endif /* ENABLE_BACKTRACE */ }; /** Invoke on_stop triggers and delete them. */ diff --git a/src/lua/fiber.c b/src/lua/fiber.c index 91898c283..7b21361d4 100644 --- a/src/lua/fiber.c +++ b/src/lua/fiber.c @@ -308,6 +308,16 @@ lbox_fiber_statof_map(struct fiber *f, void *cb_ctx, bool backtrace) backtrace_foreach(fiber_backtrace_cb, 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); #endif /* ENABLE_BACKTRACE */ } return 0; diff --git a/test/app/gh-4002-fiber-creation-backtrace.result b/test/app/gh-4002-fiber-creation-backtrace.result new file mode 100644 index 000000000..4934b82d6 --- /dev/null +++ b/test/app/gh-4002-fiber-creation-backtrace.result @@ -0,0 +1,57 @@ +-- test-run result file version 2 +yaml = require('yaml') + | --- + | ... +fiber = require('fiber') + | --- + | ... +test_run = require('test_run').new() + | --- + | ... + +local stack_len = 0 + | --- + | ... +local parent_stack_len = 0 + | --- + | ... + +test_run:cmd('setopt delimiter ";"') + | --- + | - true + | ... +foo = function() + local id = fiber.self():id() + local info = fiber.info()[id] + local stack = info.backtrace + stack_len = stack and #stack or -1 + local parent_stack = info.backtrace_parent + parent_stack_len = parent_stack and #parent_stack or -1 +end; + | --- + | ... + +test_run:cmd('setopt delimiter ""'); + | --- + | - true + | ... + +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 > 0 or 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 new file mode 100644 index 000000000..24d41a860 --- /dev/null +++ b/test/app/gh-4002-fiber-creation-backtrace.test.lua @@ -0,0 +1,27 @@ +yaml = require('yaml') +fiber = require('fiber') +test_run = require('test_run').new() + +local stack_len = 0 +local parent_stack_len = 0 + +test_run:cmd('setopt delimiter ";"') +foo = function() + local id = fiber.self():id() + local info = fiber.info()[id] + local stack = info.backtrace + stack_len = stack and #stack or -1 + local parent_stack = info.backtrace_parent + parent_stack_len = parent_stack and #parent_stack or -1 +end; + +test_run:cmd('setopt delimiter ""'); + +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 > 0 or stack_len == -1) + -- 2.31.1