From mboxrd@z Thu Jan  1 00:00:00 1970
Return-Path: <tarantool-patches-bounces@dev.tarantool.org>
Received: from [87.239.111.99] (localhost [127.0.0.1])
	by dev.tarantool.org (Postfix) with ESMTP id B44E96EC43;
	Thu,  1 Jul 2021 23:26:16 +0300 (MSK)
DKIM-Filter: OpenDKIM Filter v2.11.0 dev.tarantool.org B44E96EC43
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=tarantool.org; s=dev;
	t=1625171176; bh=YQuYn3qT0uHmLzsZYY8wHp8KVxzLZoBssccI4n8kQGM=;
	h=To:Date:In-Reply-To:References:Subject:List-Id:List-Unsubscribe:
	 List-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc:
	 From;
	b=J8l0qrInfqrWjjDXXTk0bDA1LcvFTnuo3CWX+W97EjMWgtJi96VxeZwSeun7JXHr8
	 uIoUAYG9A0HfXf4UTt+P7QK/2dko+xDZ82A3OtWm/EuR1ZtNqswOv1O68p7sMXK235
	 3x+1F3Ko1bqdta+wp4412g4yFHvaAmW5419Omj7E=
Received: from smtp60.i.mail.ru (smtp60.i.mail.ru [217.69.128.40])
 (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 DFBC36EC43
 for <tarantool-patches@dev.tarantool.org>;
 Thu,  1 Jul 2021 23:25:46 +0300 (MSK)
DKIM-Filter: OpenDKIM Filter v2.11.0 dev.tarantool.org DFBC36EC43
Received: by smtp60.i.mail.ru with esmtpa (envelope-from
 <eelchinov@tarantool.org>)
 id 1lz3Fp-0006gg-Lv; Thu, 01 Jul 2021 23:25:46 +0300
To: gorcunov@tarantool.org,
	alyapunov@tarantool.org,
	skaplun@tarantool.org
Date: Thu,  1 Jul 2021 23:24:39 +0300
Message-Id: <94f26201b661d4a2e0e47342af51c20b428fc97f.1625170992.git.elchinov.es@gmail.com>
X-Mailer: git-send-email 2.31.1
In-Reply-To: <cover.1625170992.git.elchinov.es@gmail.com>
References: <cover.1625170992.git.elchinov.es@gmail.com>
MIME-Version: 1.0
Content-Transfer-Encoding: 8bit
X-7564579A: B8F34718100C35BD
X-77F55803: 4F1203BC0FB41BD954DFF1DC42D673FB4F75AC5594ACDC16869A51A860A12816182A05F538085040D7E69172C875D3EBD1EB247EE8E52C2A05ED9139D23AFCEE91282081D53ECBCE
X-7FA49CB5: FF5795518A3D127A4AD6D5ED66289B5278DA827A17800CE771540F9ECFC94C4BEA1F7E6F0F101C67BD4B6F7A4D31EC0BCC500DACC3FED6E28638F802B75D45FF8AA50765F7900637835928C62272F24E8638F802B75D45FF36EB9D2243A4F8B5A6FCA7DBDB1FC311F39EFFDF887939037866D6147AF826D824837F1503EA47D5565025ED36919EC7117882F4460429724CE54428C33FAD305F5C1EE8F4F765FCAA867293B0326636D2E47CDBA5A96583BD4B6F7A4D31EC0BC014FD901B82EE079FA2833FD35BB23D27C277FBC8AE2E8BAA867293B0326636D2E47CDBA5A96583BA9C0B312567BB2376E601842F6C81A19E625A9149C048EEFAD5A440E159F97DBFC1D8BB4B114D1ED8FC6C240DEA7642DBF02ECDB25306B2B78CF848AE20165D0A6AB1C7CE11FEE3B2DECCBDF547A30576E601842F6C81A1F004C906525384307823802FF610243DF43C7A68FF6260569E8FC8737B5C2249EC8D19AE6D49635B68655334FD4449CB9ECD01F8117BC8BEAAAE862A0553A39223F8577A6DFFEA7CC1948A84299AD5C643847C11F186F3C59DAA53EE0834AAEE
X-B7AD71C0: AC4F5C86D027EB782CDD5689AFBDA7A2368A440D3B0F6089093C9A16E5BC824A2A04A2ABAA09D25379311020FFC8D4ADD39CE08E0D58FA782A8F7BF4B9B60A56
X-C1DE0DAB: C20DE7B7AB408E4181F030C43753B8186998911F362727C414F749A5E30D975CBCA6440478D4BA46D5E40038A42950F7E4B0EF9EED93906B9C2B6934AE262D3EE7EAB7254005DCED7532B743992DF240BDC6A1CF3F042BAD6DF99611D93F60EFCCE3E035A672CDEE699F904B3F4130E343918A1A30D5E7FCCB5012B2E24CD356
X-C8649E89: 4E36BF7865823D7055A7F0CF078B5EC49A30900B95165D34E20F2278F74481A143A6B3F1748A0FC0703F20A83A77201A4531AE03F1CCB60E4CCC70C13F2D9B4C1D7E09C32AA3244C42BDA9169CB2AAF975CB179F37B4F99160759606DA2E136A83B48618A63566E0
X-D57D3AED: 3ZO7eAau8CL7WIMRKs4sN3D3tLDjz0dLbV79QFUyzQ2Ujvy7cMT6pYYqY16iZVKkSc3dCLJ7zSJH7+u4VD18S7Vl4ZUrpaVfd2+vE6kuoey4m4VkSEu530nj6fImhcD4MUrOEAnl0W826KZ9Q+tr5ycPtXkTV4k65bRjmOUUP8cvGozZ33TWg5HZplvhhXbhDGzqmQDTd6OAevLeAnq3Ra9uf7zvY2zzsIhlcp/Y7m53TZgf2aB4JOg4gkr2biojbL9S8ysBdXhQbJZ+TAeEvyU8Q/09moKJ
X-Mailru-Sender: EFA0F3A8419EF2166B053C73E674FFD559017CA26CB6D4BB5EF4DFC66E8412EAE2527C969975515C67F54F2D6EFFC80BC77752E0C033A69E17841C44D9B5D58765F2F89A5AFDB6F16C18EFA0BB12DBB0
X-Mras: Ok
Subject: [Tarantool-patches] [PATCH 1/7] 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 <tarantool-patches.dev.tarantool.org>
List-Unsubscribe: <https://lists.tarantool.org/mailman/options/tarantool-patches>, 
 <mailto:tarantool-patches-request@dev.tarantool.org?subject=unsubscribe>
List-Archive: <https://lists.tarantool.org/pipermail/tarantool-patches/>
List-Post: <mailto:tarantool-patches@dev.tarantool.org>
List-Help: <mailto:tarantool-patches-request@dev.tarantool.org?subject=help>
List-Subscribe: <https://lists.tarantool.org/mailman/listinfo/tarantool-patches>, 
 <mailto:tarantool-patches-request@dev.tarantool.org?subject=subscribe>
From: Egor Elchinov via Tarantool-patches <tarantool-patches@dev.tarantool.org>
Reply-To: eelchinov@tarantool.org
Cc: tarantool-patches@dev.tarantool.org
Errors-To: tarantool-patches-bounces@dev.tarantool.org
Sender: "Tarantool-patches" <tarantool-patches-bounces@dev.tarantool.org>

From: Egor Elchinov <eelchinov@tarantool.org>

From: Egor Elchinov <elchinov.es@gmail.com>

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.

Needed for: #4002
---
 src/lib/core/backtrace.cc                     | 66 +++++++++++++++++++
 src/lib/core/backtrace.h                      |  6 ++
 src/lib/core/fiber.c                          |  8 +++
 src/lib/core/fiber.h                          | 16 +++++
 src/lua/fiber.c                               |  9 +++
 .../gh-4002-fiber-creation-backtrace.result   | 57 ++++++++++++++++
 .../gh-4002-fiber-creation-backtrace.test.lua | 27 ++++++++
 7 files changed, 189 insertions(+)
 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..276933ad6 100644
--- a/src/lib/core/backtrace.cc
+++ b/src/lib/core/backtrace.cc
@@ -432,6 +432,72 @@ 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
+fast_trace_collect(void **ip_buf, int limit)
+{
+	memset(ip_buf, 0, limit * sizeof(*ip_buf));
+	unw_backtrace(ip_buf, limit);
+}
+
+/**
+ * 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
+fast_trace_foreach(backtrace_cb cb, void **ip_buf, int limit, void *cb_ctx)
+{
+	static __thread char proc_name[BACKTRACE_NAME_MAX];
+	int frame_no = 0;
+	unw_word_t ip = 0, offset = 0;
+	unw_proc_info_t pi;
+	int ret = 0;
+	char* proc = NULL;
+
+	unw_accessors_t* acc = unw_get_accessors(unw_local_addr_space);
+	assert(acc);
+
+	for (frame_no = 0; 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 || (frame_no > 0 &&
+		    cb(frame_no - 1, (void *)ip, proc,
+	 	       (size_t)offset, cb_ctx) != 0))
+			break;
+	}
+
+#ifndef TARGET_OS_DARWIN
+	if (ret != 0)
+		say_debug("unwinding error: %s", unw_strerror(ret));
+#else
+	if (ret != 0)
+		say_debug("unwinding error: %i", ret);
+#endif
+}
+
 void
 print_backtrace(void)
 {
diff --git a/src/lib/core/backtrace.h b/src/lib/core/backtrace.h
index e0ae56be4..1560f03e3 100644
--- a/src/lib/core/backtrace.h
+++ b/src/lib/core/backtrace.h
@@ -55,6 +55,12 @@ backtrace_foreach(backtrace_cb cb, coro_context *coro_ctx, void *cb_ctx);
 void
 backtrace_proc_cache_clear(void);
 
+void
+fast_trace_collect(void **ip_buf, int limit);
+
+void
+fast_trace_foreach(backtrace_cb cb, void **ip_buf, int limit, void *cb_ctx);
+
 #endif /* ENABLE_BACKTRACE */
 
 #if defined(__cplusplus)
diff --git a/src/lib/core/fiber.c b/src/lib/core/fiber.c
index 759c7da6a..73500e043 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 <x86intrin.h> /* __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
+	fast_trace_collect(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..6a155c0fe 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..dc7051edd 100644
--- a/src/lua/fiber.c
+++ b/src/lua/fiber.c
@@ -308,6 +308,15 @@ 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);
+		fast_trace_foreach(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


From mboxrd@z Thu Jan  1 00:00:00 1970
Return-Path: <tarantool-patches-bounces@dev.tarantool.org>
Received: from [87.239.111.99] (localhost [127.0.0.1])
	by dev.tarantool.org (Postfix) with ESMTP id 5B6F96EC40;
	Thu,  1 Jul 2021 23:45:10 +0300 (MSK)
DKIM-Filter: OpenDKIM Filter v2.11.0 dev.tarantool.org 5B6F96EC40
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=tarantool.org; s=dev;
	t=1625172310; bh=YQuYn3qT0uHmLzsZYY8wHp8KVxzLZoBssccI4n8kQGM=;
	h=To:Date:In-Reply-To:References:Subject:List-Id:List-Unsubscribe:
	 List-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc:
	 From;
	b=M16dY/JzGPrwRPArc+5w1O9catizBudVw/BsN2zLXguWeqd7tuEabh/F0o8esOOMY
	 yWBQo69ZuUhDATG84UGURtnCVC67S0AZYk9aKQ7UFqV4s6HVINoFOgC2AX1ZGF770S
	 d0znx9zgzFht73OBkRYR+/oxPaImy6O1NaqaDujk=
Received: from smtp29.i.mail.ru (smtp29.i.mail.ru [94.100.177.89])
 (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 565856EC40
 for <tarantool-patches@dev.tarantool.org>;
 Thu,  1 Jul 2021 23:45:06 +0300 (MSK)
DKIM-Filter: OpenDKIM Filter v2.11.0 dev.tarantool.org 565856EC40
Received: by smtp29.i.mail.ru with esmtpa (envelope-from
 <eelchinov@tarantool.org>)
 id 1lz3YW-0007cW-UO; Thu, 01 Jul 2021 23:45:05 +0300
To: gorcunov@tarantool.org,
	alyapunov@tarantool.org,
	skaplun@tarantool.org
Date: Thu,  1 Jul 2021 23:44:54 +0300
Message-ID: <94f26201b661d4a2e0e47342af51c20b428fc97f.1625170992.git.elchinov.es@gmail.com>
X-Mailer: git-send-email 2.31.1
In-Reply-To: <cover.1625170992.git.elchinov.es@gmail.com>
References: <cover.1625170992.git.elchinov.es@gmail.com>
MIME-Version: 1.0
Content-Transfer-Encoding: 8bit
X-7564579A: B8F34718100C35BD
X-77F55803: 4F1203BC0FB41BD954DFF1DC42D673FB8EEAA58EF109ED2DCA3B880632F394B8182A05F538085040D9816A809457BF7F33E59C1F06B01E0AB70C56FB41B028F7C0F962E8BB342B56
X-7FA49CB5: FF5795518A3D127A4AD6D5ED66289B5278DA827A17800CE771540F9ECFC94C4BEA1F7E6F0F101C67BD4B6F7A4D31EC0BCC500DACC3FED6E28638F802B75D45FF8AA50765F7900637835928C62272F24E8638F802B75D45FF36EB9D2243A4F8B5A6FCA7DBDB1FC311F39EFFDF887939037866D6147AF826D8C2044F03932030693C0F29408E097D37117882F4460429724CE54428C33FAD305F5C1EE8F4F765FCAA867293B0326636D2E47CDBA5A96583BD4B6F7A4D31EC0BC014FD901B82EE079FA2833FD35BB23D27C277FBC8AE2E8BAA867293B0326636D2E47CDBA5A96583BA9C0B312567BB2376E601842F6C81A19E625A9149C048EEFAD5A440E159F97D4782AAF36435267CD8FC6C240DEA7642DBF02ECDB25306B2B78CF848AE20165D0A6AB1C7CE11FEE3B2DECCBDF547A30576E601842F6C81A1F004C906525384307823802FF610243DF43C7A68FF6260569E8FC8737B5C2249EC8D19AE6D49635B68655334FD4449CB9ECD01F8117BC8BEAAAE862A0553A39223F8577A6DFFEA7CB24F08513AFFAC7943847C11F186F3C59DAA53EE0834AAEE
X-B7AD71C0: AC4F5C86D027EB782CDD5689AFBDA7A2368A440D3B0F6089093C9A16E5BC824A2A04A2ABAA09D25379311020FFC8D4ADD39CE08E0D58FA786E4C97BED636D9A3
X-C1DE0DAB: C20DE7B7AB408E4181F030C43753B8186998911F362727C414F749A5E30D975CBCA6440478D4BA4601D68DCE53748494E5A125D8894138C19C2B6934AE262D3EE7EAB7254005DCED7532B743992DF240BDC6A1CF3F042BAD6DF99611D93F60EFB1880DAAC9D7D4AB699F904B3F4130E343918A1A30D5E7FCCB5012B2E24CD356
X-C8649E89: 4E36BF7865823D7055A7F0CF078B5EC49A30900B95165D34E20F2278F74481A143A6B3F1748A0FC0703F20A83A77201A4531AE03F1CCB60E4CCC70C13F2D9B4C1D7E09C32AA3244C580BD5A8BE29EA3C199B10902965F6D564EE5813BBCA3A9D83B48618A63566E0
X-D57D3AED: 3ZO7eAau8CL7WIMRKs4sN3D3tLDjz0dLbV79QFUyzQ2Ujvy7cMT6pYYqY16iZVKkSc3dCLJ7zSJH7+u4VD18S7Vl4ZUrpaVfd2+vE6kuoey4m4VkSEu530nj6fImhcD4MUrOEAnl0W826KZ9Q+tr5ycPtXkTV4k65bRjmOUUP8cvGozZ33TWg5HZplvhhXbhDGzqmQDTd6OAevLeAnq3Ra9uf7zvY2zzsIhlcp/Y7m53TZgf2aB4JOg4gkr2bioj1IeO6ekkXq3X6WdzjRnXYg==
X-Mailru-Sender: EFA0F3A8419EF2166B053C73E674FFD5F34E170AD25B48BD8B7F9FD443D72E4CE2527C969975515C67F54F2D6EFFC80BC77752E0C033A69E17841C44D9B5D58765F2F89A5AFDB6F16C18EFA0BB12DBB0
X-Mras: Ok
Subject: [Tarantool-patches] [PATCH 1/7] 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 <tarantool-patches.dev.tarantool.org>
List-Unsubscribe: <https://lists.tarantool.org/mailman/options/tarantool-patches>, 
 <mailto:tarantool-patches-request@dev.tarantool.org?subject=unsubscribe>
List-Archive: <https://lists.tarantool.org/pipermail/tarantool-patches/>
List-Post: <mailto:tarantool-patches@dev.tarantool.org>
List-Help: <mailto:tarantool-patches-request@dev.tarantool.org?subject=help>
List-Subscribe: <https://lists.tarantool.org/mailman/listinfo/tarantool-patches>, 
 <mailto:tarantool-patches-request@dev.tarantool.org?subject=subscribe>
From: Egor Elchinov via Tarantool-patches <tarantool-patches@dev.tarantool.org>
Reply-To: eelchinov@tarantool.org
Cc: tarantool-patches@dev.tarantool.org
Errors-To: tarantool-patches-bounces@dev.tarantool.org
Sender: "Tarantool-patches" <tarantool-patches-bounces@dev.tarantool.org>
Message-ID: <20210701204454.YmEMu_A2CMhxwNvZkMtjyHFpszg92Wbc9Z1s-u77fao@z>

From: Egor Elchinov <eelchinov@tarantool.org>

From: Egor Elchinov <elchinov.es@gmail.com>

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.

Needed for: #4002
---
 src/lib/core/backtrace.cc                     | 66 +++++++++++++++++++
 src/lib/core/backtrace.h                      |  6 ++
 src/lib/core/fiber.c                          |  8 +++
 src/lib/core/fiber.h                          | 16 +++++
 src/lua/fiber.c                               |  9 +++
 .../gh-4002-fiber-creation-backtrace.result   | 57 ++++++++++++++++
 .../gh-4002-fiber-creation-backtrace.test.lua | 27 ++++++++
 7 files changed, 189 insertions(+)
 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..276933ad6 100644
--- a/src/lib/core/backtrace.cc
+++ b/src/lib/core/backtrace.cc
@@ -432,6 +432,72 @@ 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
+fast_trace_collect(void **ip_buf, int limit)
+{
+	memset(ip_buf, 0, limit * sizeof(*ip_buf));
+	unw_backtrace(ip_buf, limit);
+}
+
+/**
+ * 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
+fast_trace_foreach(backtrace_cb cb, void **ip_buf, int limit, void *cb_ctx)
+{
+	static __thread char proc_name[BACKTRACE_NAME_MAX];
+	int frame_no = 0;
+	unw_word_t ip = 0, offset = 0;
+	unw_proc_info_t pi;
+	int ret = 0;
+	char* proc = NULL;
+
+	unw_accessors_t* acc = unw_get_accessors(unw_local_addr_space);
+	assert(acc);
+
+	for (frame_no = 0; 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 || (frame_no > 0 &&
+		    cb(frame_no - 1, (void *)ip, proc,
+	 	       (size_t)offset, cb_ctx) != 0))
+			break;
+	}
+
+#ifndef TARGET_OS_DARWIN
+	if (ret != 0)
+		say_debug("unwinding error: %s", unw_strerror(ret));
+#else
+	if (ret != 0)
+		say_debug("unwinding error: %i", ret);
+#endif
+}
+
 void
 print_backtrace(void)
 {
diff --git a/src/lib/core/backtrace.h b/src/lib/core/backtrace.h
index e0ae56be4..1560f03e3 100644
--- a/src/lib/core/backtrace.h
+++ b/src/lib/core/backtrace.h
@@ -55,6 +55,12 @@ backtrace_foreach(backtrace_cb cb, coro_context *coro_ctx, void *cb_ctx);
 void
 backtrace_proc_cache_clear(void);
 
+void
+fast_trace_collect(void **ip_buf, int limit);
+
+void
+fast_trace_foreach(backtrace_cb cb, void **ip_buf, int limit, void *cb_ctx);
+
 #endif /* ENABLE_BACKTRACE */
 
 #if defined(__cplusplus)
diff --git a/src/lib/core/fiber.c b/src/lib/core/fiber.c
index 759c7da6a..73500e043 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 <x86intrin.h> /* __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
+	fast_trace_collect(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..6a155c0fe 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..dc7051edd 100644
--- a/src/lua/fiber.c
+++ b/src/lua/fiber.c
@@ -308,6 +308,15 @@ 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);
+		fast_trace_foreach(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


From mboxrd@z Thu Jan  1 00:00:00 1970
Return-Path: <tarantool-patches-bounces@dev.tarantool.org>
Received: from [87.239.111.99] (localhost [127.0.0.1])
	by dev.tarantool.org (Postfix) with ESMTP id 5766D6EC40;
	Fri,  2 Jul 2021 10:00:32 +0300 (MSK)
DKIM-Filter: OpenDKIM Filter v2.11.0 dev.tarantool.org 5766D6EC40
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=tarantool.org; s=dev;
	t=1625209232; bh=YQuYn3qT0uHmLzsZYY8wHp8KVxzLZoBssccI4n8kQGM=;
	h=To:Date:In-Reply-To:References:Subject:List-Id:List-Unsubscribe:
	 List-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:
	 From;
	b=pBecTwJR5MRNRJFKFShgQ7wkwPrn00iH+uCSAcM+yU5+LEvLoF8EGqH2xhl95lZ/n
	 FuiVzXSpzgzz5UZgAi52huOMVqlR4N9bPaAAWbGDHsN2t4SUfQN70VR1jkg1vwW2+v
	 lKiaAXPBCEve+0dr6hb6MQxCzdUzeAT09chtoBUg=
Received: from smtp63.i.mail.ru (smtp63.i.mail.ru [217.69.128.43])
 (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 476196EC40
 for <tarantool-patches@dev.tarantool.org>;
 Fri,  2 Jul 2021 10:00:30 +0300 (MSK)
DKIM-Filter: OpenDKIM Filter v2.11.0 dev.tarantool.org 476196EC40
Received: by smtp63.i.mail.ru with esmtpa (envelope-from
 <eelchinov@tarantool.org>)
 id 1lzDA5-00074l-2m; Fri, 02 Jul 2021 10:00:29 +0300
To: tarantool-patches@dev.tarantool.org
Date: Fri,  2 Jul 2021 10:00:12 +0300
Message-ID: <94f26201b661d4a2e0e47342af51c20b428fc97f.1625170992.git.elchinov.es@gmail.com>
X-Mailer: git-send-email 2.31.1
In-Reply-To: <cover.1625170992.git.elchinov.es@gmail.com>
References: <cover.1625170992.git.elchinov.es@gmail.com>
MIME-Version: 1.0
Content-Transfer-Encoding: 8bit
X-4EC0790: 10
X-7564579A: B8F34718100C35BD
X-77F55803: 4F1203BC0FB41BD954DFF1DC42D673FB703477AD6D36A6E3C7EF2853D6A1C7C1182A05F538085040C9D7D000660CDDBAA0B68B436D3D3289865C4C055355256863C92A6E3A1CEEAB
X-7FA49CB5: FF5795518A3D127A4AD6D5ED66289B5278DA827A17800CE771540F9ECFC94C4BEA1F7E6F0F101C67BD4B6F7A4D31EC0BCC500DACC3FED6E28638F802B75D45FF8AA50765F7900637835928C62272F24E8638F802B75D45FF36EB9D2243A4F8B5A6FCA7DBDB1FC311F39EFFDF887939037866D6147AF826D83E2E709E113583657B9903868FF2A1FF117882F4460429724CE54428C33FAD305F5C1EE8F4F765FCAA867293B0326636D2E47CDBA5A96583BD4B6F7A4D31EC0BC014FD901B82EE079FA2833FD35BB23D27C277FBC8AE2E8BAA867293B0326636D2E47CDBA5A96583BA9C0B312567BB2376E601842F6C81A19E625A9149C048EEFAD5A440E159F97D9100238FE36DC7A2D8FC6C240DEA7642DBF02ECDB25306B2B78CF848AE20165D0A6AB1C7CE11FEE3DA7BFA4571439BB26E0066C2D8992A16C4224003CC836476EA7A3FFF5B025636E2021AF6380DFAD1A18204E546F3947CB11811A4A51E3B096D1867E19FE1407959CC434672EE6371089D37D7C0E48F6C8AA50765F7900637A7EFCB0EB5ACB161EFF80C71ABB335746BA297DBC24807EABDAD6C7F3747799A
X-C1DE0DAB: C20DE7B7AB408E4181F030C43753B8186998911F362727C414F749A5E30D975CBCA6440478D4BA46F51AD7B747B15FAD3604301A255A25BB9C2B6934AE262D3EE7EAB7254005DCED7532B743992DF240BDC6A1CF3F042BAD6DF99611D93F60EFB1D0AC14175A0720699F904B3F4130E343918A1A30D5E7FCCB5012B2E24CD356
X-C8649E89: 4E36BF7865823D7055A7F0CF078B5EC49A30900B95165D34E20F2278F74481A143A6B3F1748A0FC0703F20A83A77201A4531AE03F1CCB60E4CCC70C13F2D9B4C1D7E09C32AA3244C5D90DB45D4633D2AC774F0FFF5F54DD63E8609A02908F271729B2BEF169E0186
X-D57D3AED: 3ZO7eAau8CL7WIMRKs4sN3D3tLDjz0dLbV79QFUyzQ2Ujvy7cMT6pYYqY16iZVKkSc3dCLJ7zSJH7+u4VD18S7Vl4ZUrpaVfd2+vE6kuoey4m4VkSEu530nj6fImhcD4MUrOEAnl0W826KZ9Q+tr5ycPtXkTV4k65bRjmOUUP8cvGozZ33TWg5HZplvhhXbhDGzqmQDTd6OAevLeAnq3Ra9uf7zvY2zzsIhlcp/Y7m53TZgf2aB4JOg4gkr2bioj/yEXjM+E11BT/ZFlI+ZatA==
X-Mailru-Sender: EFA0F3A8419EF2166B053C73E674FFD5A955ED5F69CBD20CFEEB47BB4A5B05A5E2527C969975515C67F54F2D6EFFC80BC77752E0C033A69E17841C44D9B5D58765F2F89A5AFDB6F16C18EFA0BB12DBB0
X-Mras: Ok
Subject: [Tarantool-patches] [PATCH 1/7] 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 <tarantool-patches.dev.tarantool.org>
List-Unsubscribe: <https://lists.tarantool.org/mailman/options/tarantool-patches>, 
 <mailto:tarantool-patches-request@dev.tarantool.org?subject=unsubscribe>
List-Archive: <https://lists.tarantool.org/pipermail/tarantool-patches/>
List-Post: <mailto:tarantool-patches@dev.tarantool.org>
List-Help: <mailto:tarantool-patches-request@dev.tarantool.org?subject=help>
List-Subscribe: <https://lists.tarantool.org/mailman/listinfo/tarantool-patches>, 
 <mailto:tarantool-patches-request@dev.tarantool.org?subject=subscribe>
From: Egor Elchinov via Tarantool-patches <tarantool-patches@dev.tarantool.org>
Reply-To: eelchinov@tarantool.org
Errors-To: tarantool-patches-bounces@dev.tarantool.org
Sender: "Tarantool-patches" <tarantool-patches-bounces@dev.tarantool.org>
Message-ID: <20210702070012.Rtte6mYYn6pae7g8ellUgtjrakfBQ8Nr6J0mVJsO4G0@z>

From: Egor Elchinov <eelchinov@tarantool.org>

From: Egor Elchinov <elchinov.es@gmail.com>

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.

Needed for: #4002
---
 src/lib/core/backtrace.cc                     | 66 +++++++++++++++++++
 src/lib/core/backtrace.h                      |  6 ++
 src/lib/core/fiber.c                          |  8 +++
 src/lib/core/fiber.h                          | 16 +++++
 src/lua/fiber.c                               |  9 +++
 .../gh-4002-fiber-creation-backtrace.result   | 57 ++++++++++++++++
 .../gh-4002-fiber-creation-backtrace.test.lua | 27 ++++++++
 7 files changed, 189 insertions(+)
 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..276933ad6 100644
--- a/src/lib/core/backtrace.cc
+++ b/src/lib/core/backtrace.cc
@@ -432,6 +432,72 @@ 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
+fast_trace_collect(void **ip_buf, int limit)
+{
+	memset(ip_buf, 0, limit * sizeof(*ip_buf));
+	unw_backtrace(ip_buf, limit);
+}
+
+/**
+ * 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
+fast_trace_foreach(backtrace_cb cb, void **ip_buf, int limit, void *cb_ctx)
+{
+	static __thread char proc_name[BACKTRACE_NAME_MAX];
+	int frame_no = 0;
+	unw_word_t ip = 0, offset = 0;
+	unw_proc_info_t pi;
+	int ret = 0;
+	char* proc = NULL;
+
+	unw_accessors_t* acc = unw_get_accessors(unw_local_addr_space);
+	assert(acc);
+
+	for (frame_no = 0; 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 || (frame_no > 0 &&
+		    cb(frame_no - 1, (void *)ip, proc,
+	 	       (size_t)offset, cb_ctx) != 0))
+			break;
+	}
+
+#ifndef TARGET_OS_DARWIN
+	if (ret != 0)
+		say_debug("unwinding error: %s", unw_strerror(ret));
+#else
+	if (ret != 0)
+		say_debug("unwinding error: %i", ret);
+#endif
+}
+
 void
 print_backtrace(void)
 {
diff --git a/src/lib/core/backtrace.h b/src/lib/core/backtrace.h
index e0ae56be4..1560f03e3 100644
--- a/src/lib/core/backtrace.h
+++ b/src/lib/core/backtrace.h
@@ -55,6 +55,12 @@ backtrace_foreach(backtrace_cb cb, coro_context *coro_ctx, void *cb_ctx);
 void
 backtrace_proc_cache_clear(void);
 
+void
+fast_trace_collect(void **ip_buf, int limit);
+
+void
+fast_trace_foreach(backtrace_cb cb, void **ip_buf, int limit, void *cb_ctx);
+
 #endif /* ENABLE_BACKTRACE */
 
 #if defined(__cplusplus)
diff --git a/src/lib/core/fiber.c b/src/lib/core/fiber.c
index 759c7da6a..73500e043 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 <x86intrin.h> /* __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
+	fast_trace_collect(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..6a155c0fe 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..dc7051edd 100644
--- a/src/lua/fiber.c
+++ b/src/lua/fiber.c
@@ -308,6 +308,15 @@ 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);
+		fast_trace_foreach(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


From mboxrd@z Thu Jan  1 00:00:00 1970
Return-Path: <tarantool-patches-bounces@dev.tarantool.org>
Received: from [87.239.111.99] (localhost [127.0.0.1])
	by dev.tarantool.org (Postfix) with ESMTP id 56CA16EC40;
	Fri,  2 Jul 2021 10:11:40 +0300 (MSK)
DKIM-Filter: OpenDKIM Filter v2.11.0 dev.tarantool.org 56CA16EC40
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=tarantool.org; s=dev;
	t=1625209900; bh=YQuYn3qT0uHmLzsZYY8wHp8KVxzLZoBssccI4n8kQGM=;
	h=To:Date:In-Reply-To:References:Subject:List-Id:List-Unsubscribe:
	 List-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:
	 From;
	b=n3lIHzxHZ5BjPXc99sE93a0Xko3cdgDijzLqI/5rTG8/4niZETvz++MTKYO1rLosC
	 h3E9BwURNR0c03AOD+byj2Bh8GeLcya9HVvhv6aFh4PkoOqcMou/eePRYjeYIzMG9u
	 1tH0mhmck9BAHhVba3R3sk4DeNUlLngiPqT0L9YM=
Received: from smtp36.i.mail.ru (smtp36.i.mail.ru [94.100.177.96])
 (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 66DDF6EC40
 for <tarantool-patches@dev.tarantool.org>;
 Fri,  2 Jul 2021 10:11:38 +0300 (MSK)
DKIM-Filter: OpenDKIM Filter v2.11.0 dev.tarantool.org 66DDF6EC40
Received: by smtp36.i.mail.ru with esmtpa (envelope-from
 <eelchinov@tarantool.org>)
 id 1lzDKr-0002KV-JR; Fri, 02 Jul 2021 10:11:38 +0300
To: tarantool-patches@dev.tarantool.org
Date: Fri,  2 Jul 2021 10:11:11 +0300
Message-ID: <94f26201b661d4a2e0e47342af51c20b428fc97f.1625170992.git.elchinov.es@gmail.com>
X-Mailer: git-send-email 2.31.1
In-Reply-To: <cover.1625170992.git.elchinov.es@gmail.com>
References: <cover.1625170992.git.elchinov.es@gmail.com>
MIME-Version: 1.0
Content-Transfer-Encoding: 8bit
X-4EC0790: 10
X-7564579A: 646B95376F6C166E
X-77F55803: 4F1203BC0FB41BD954DFF1DC42D673FB2F1AA0EB8A504C8721532AB396CDCF09182A05F538085040EC990D233A499A7A42DCD909715EAA0774AE9244F56DFD1530B974E646396887
X-7FA49CB5: FF5795518A3D127A4AD6D5ED66289B5278DA827A17800CE771540F9ECFC94C4BEA1F7E6F0F101C67BD4B6F7A4D31EC0BCC500DACC3FED6E28638F802B75D45FF8AA50765F7900637B12C6B1582157D838638F802B75D45FF36EB9D2243A4F8B5A6FCA7DBDB1FC311F39EFFDF887939037866D6147AF826D8A358C9F230BC99CF37017295FDBEED15117882F4460429724CE54428C33FAD305F5C1EE8F4F765FCAA867293B0326636D2E47CDBA5A96583BD4B6F7A4D31EC0BC014FD901B82EE079FA2833FD35BB23D27C277FBC8AE2E8BAA867293B0326636D2E47CDBA5A96583BA9C0B312567BB2376E601842F6C81A19E625A9149C048EEC8105B04EFE07628B341D7040ADD27A2D8FC6C240DEA7642DBF02ECDB25306B2B78CF848AE20165D0A6AB1C7CE11FEE34D3DDB508812D9002D242C3BD2E3F4C6C4224003CC836476EA7A3FFF5B025636E2021AF6380DFAD1A18204E546F3947CB11811A4A51E3B096D1867E19FE1407959CC434672EE6371089D37D7C0E48F6C8AA50765F7900637A7EFCB0EB5ACB161EFF80C71ABB335746BA297DBC24807EABDAD6C7F3747799A
X-C1DE0DAB: C20DE7B7AB408E4181F030C43753B8186998911F362727C414F749A5E30D975CBCA6440478D4BA463429A1C42530B0822419644BE73FD01E9C2B6934AE262D3EE7EAB7254005DCED7532B743992DF240BDC6A1CF3F042BAD6DF99611D93F60EF309DFB797F6729CB699F904B3F4130E343918A1A30D5E7FCCB5012B2E24CD356
X-C8649E89: 4E36BF7865823D7055A7F0CF078B5EC49A30900B95165D34F6CC1FD3366963363D3A2A4722C495CDA41D8344E766ED13CAEA230F2929D7D5C4B2EB0D681792CB1D7E09C32AA3244CEF0A115FB3AFEC8EEC7477F455A60ECAA95CA90A1D8AC565729B2BEF169E0186
X-D57D3AED: 3ZO7eAau8CL7WIMRKs4sN3D3tLDjz0dLbV79QFUyzQ2Ujvy7cMT6pYYqY16iZVKkSc3dCLJ7zSJH7+u4VD18S7Vl4ZUrpaVfd2+vE6kuoey4m4VkSEu530nj6fImhcD4MUrOEAnl0W826KZ9Q+tr5ycPtXkTV4k65bRjmOUUP8cvGozZ33TWg5HZplvhhXbhDGzqmQDTd6OAevLeAnq3Ra9uf7zvY2zzsIhlcp/Y7m53TZgf2aB4JOg4gkr2bioj/yEXjM+E11DsSlJAr37AFA==
X-Mailru-Sender: EFA0F3A8419EF2166B053C73E674FFD52DFC3F2A2B41AEECE3750603D2A1B65FE2527C969975515C67F54F2D6EFFC80BC77752E0C033A69E17841C44D9B5D58765F2F89A5AFDB6F16C18EFA0BB12DBB0
X-Mras: Ok
Subject: [Tarantool-patches] [PATCH v2 1/7] 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 <tarantool-patches.dev.tarantool.org>
List-Unsubscribe: <https://lists.tarantool.org/mailman/options/tarantool-patches>, 
 <mailto:tarantool-patches-request@dev.tarantool.org?subject=unsubscribe>
List-Archive: <https://lists.tarantool.org/pipermail/tarantool-patches/>
List-Post: <mailto:tarantool-patches@dev.tarantool.org>
List-Help: <mailto:tarantool-patches-request@dev.tarantool.org?subject=help>
List-Subscribe: <https://lists.tarantool.org/mailman/listinfo/tarantool-patches>, 
 <mailto:tarantool-patches-request@dev.tarantool.org?subject=subscribe>
From: Egor Elchinov via Tarantool-patches <tarantool-patches@dev.tarantool.org>
Reply-To: eelchinov@tarantool.org
Errors-To: tarantool-patches-bounces@dev.tarantool.org
Sender: "Tarantool-patches" <tarantool-patches-bounces@dev.tarantool.org>
Message-ID: <20210702071111.TTBiBPFn4zkt1eu9d4QyH6K1WtAsHsGjW0fQlzAOYrY@z>

From: Egor Elchinov <eelchinov@tarantool.org>

From: Egor Elchinov <elchinov.es@gmail.com>

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.

Needed for: #4002
---
 src/lib/core/backtrace.cc                     | 66 +++++++++++++++++++
 src/lib/core/backtrace.h                      |  6 ++
 src/lib/core/fiber.c                          |  8 +++
 src/lib/core/fiber.h                          | 16 +++++
 src/lua/fiber.c                               |  9 +++
 .../gh-4002-fiber-creation-backtrace.result   | 57 ++++++++++++++++
 .../gh-4002-fiber-creation-backtrace.test.lua | 27 ++++++++
 7 files changed, 189 insertions(+)
 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..276933ad6 100644
--- a/src/lib/core/backtrace.cc
+++ b/src/lib/core/backtrace.cc
@@ -432,6 +432,72 @@ 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
+fast_trace_collect(void **ip_buf, int limit)
+{
+	memset(ip_buf, 0, limit * sizeof(*ip_buf));
+	unw_backtrace(ip_buf, limit);
+}
+
+/**
+ * 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
+fast_trace_foreach(backtrace_cb cb, void **ip_buf, int limit, void *cb_ctx)
+{
+	static __thread char proc_name[BACKTRACE_NAME_MAX];
+	int frame_no = 0;
+	unw_word_t ip = 0, offset = 0;
+	unw_proc_info_t pi;
+	int ret = 0;
+	char* proc = NULL;
+
+	unw_accessors_t* acc = unw_get_accessors(unw_local_addr_space);
+	assert(acc);
+
+	for (frame_no = 0; 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 || (frame_no > 0 &&
+		    cb(frame_no - 1, (void *)ip, proc,
+	 	       (size_t)offset, cb_ctx) != 0))
+			break;
+	}
+
+#ifndef TARGET_OS_DARWIN
+	if (ret != 0)
+		say_debug("unwinding error: %s", unw_strerror(ret));
+#else
+	if (ret != 0)
+		say_debug("unwinding error: %i", ret);
+#endif
+}
+
 void
 print_backtrace(void)
 {
diff --git a/src/lib/core/backtrace.h b/src/lib/core/backtrace.h
index e0ae56be4..1560f03e3 100644
--- a/src/lib/core/backtrace.h
+++ b/src/lib/core/backtrace.h
@@ -55,6 +55,12 @@ backtrace_foreach(backtrace_cb cb, coro_context *coro_ctx, void *cb_ctx);
 void
 backtrace_proc_cache_clear(void);
 
+void
+fast_trace_collect(void **ip_buf, int limit);
+
+void
+fast_trace_foreach(backtrace_cb cb, void **ip_buf, int limit, void *cb_ctx);
+
 #endif /* ENABLE_BACKTRACE */
 
 #if defined(__cplusplus)
diff --git a/src/lib/core/fiber.c b/src/lib/core/fiber.c
index 759c7da6a..73500e043 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 <x86intrin.h> /* __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
+	fast_trace_collect(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..6a155c0fe 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..dc7051edd 100644
--- a/src/lua/fiber.c
+++ b/src/lua/fiber.c
@@ -308,6 +308,15 @@ 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);
+		fast_trace_foreach(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