Skip to content

Commit 2c7002b

Browse files
committed
Better debug tools
1 parent f696c50 commit 2c7002b

4 files changed

Lines changed: 177 additions & 14 deletions

File tree

io/iouring-wrapper.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ class iouringEngine : public MasterEventEngine, public CascadingEventEngine, pub
188188
ioCtx timer_ctx(true, false);
189189
__kernel_timespec ts;
190190
auto usec = timeout.timeout_us();
191-
if (usec < std::numeric_limits<int64_t>::max()) {
191+
if (usec < (uint64_t)std::numeric_limits<int64_t>::max()) {
192192
sqe->flags |= IOSQE_IO_LINK;
193193
ts = usec_to_timespec(usec);
194194
sqe = _get_sqe();
@@ -334,7 +334,7 @@ class iouringEngine : public MasterEventEngine, public CascadingEventEngine, pub
334334

335335
ssize_t wait_and_fire_events(uint64_t timeout) override {
336336
// Prepare own timeout
337-
if (timeout > std::numeric_limits<int64_t>::max()) {
337+
if (timeout > (uint64_t)std::numeric_limits<int64_t>::max()) {
338338
timeout = std::numeric_limits<int64_t>::max();
339339
}
340340

thread/thread.cpp

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,51 @@ namespace photon
169169
void* _ptr;
170170
};
171171

172+
#if defined(__has_feature)
173+
# if __has_feature(address_sanitizer) // for clang
174+
# define __SANITIZE_ADDRESS__ // GCC already sets this
175+
# endif
176+
#endif
177+
178+
#ifdef __SANITIZE_ADDRESS__
179+
extern "C" {
180+
// Check out sanitizer/asan-interface.h in compiler-rt for documentation.
181+
void __sanitizer_start_switch_fiber(void** fake_stack_save, const void* bottom,
182+
size_t size);
183+
void __sanitizer_finish_switch_fiber(void* fake_stack_save,
184+
const void** bottom_old, size_t* size_old);
185+
}
186+
187+
static void asan_start(void** save, thread* to) {
188+
void* bottom = to->buf ? to->buf : to->stackful_alloc_top;
189+
__sanitizer_start_switch_fiber(save, bottom,
190+
to->stack_size);
191+
}
192+
193+
static void asan_finish(void* save) {
194+
__sanitizer_finish_switch_fiber(save, nullptr, nullptr);
195+
}
196+
197+
#define ASAN_START() asan_finish((void*)nullptr);
198+
199+
#define ASAN_SWITCH(to) \
200+
void* __save; \
201+
asan_start(&__save, to); \
202+
DEFER({ asan_finish(__save); });
203+
204+
#define ASAN_DIE_SWITCH(to) \
205+
asan_start(nullptr, to);
206+
207+
#else
208+
#define ASAN_START(ptr)
209+
#define ASAN_SWITCH(to)
210+
#define ASAN_DIE_SWITCH(to)
211+
#endif
212+
213+
static void _asan_start() asm("_asan_start");
214+
215+
__attribute__((used)) static void _asan_start() { ASAN_START(); }
216+
172217
struct thread_list;
173218
struct thread : public intrusive_list_node<thread> {
174219
volatile vcpu_t* vcpu;
@@ -695,6 +740,7 @@ R"(
695740
);
696741

697742
inline void switch_context(thread* from, thread* to) {
743+
ASAN_SWITCH(to);
698744
prepare_switch(from, to);
699745
auto _t_ = to->stack.pointer_ref();
700746
register auto f asm("rsi") = from->stack.pointer_ref();
@@ -708,6 +754,7 @@ R"(
708754

709755
inline void switch_context_defer(thread* from, thread* to,
710756
void (*defer)(void*), void* arg) {
757+
ASAN_SWITCH(to);
711758
prepare_switch(from, to);
712759
auto _t_ = to->stack.pointer_ref();
713760
register auto f asm("rcx") = from->stack.pointer_ref();
@@ -747,6 +794,7 @@ R"(
747794

748795
DEF_ASM_FUNC(_photon_thread_stub)
749796
R"(
797+
call _asan_start
750798
mov 0x40(%rbp), %rcx
751799
movq $0, 0x40(%rbp)
752800
call *0x48(%rbp)
@@ -757,6 +805,7 @@ R"(
757805
);
758806

759807
inline void switch_context(thread* from, thread* to) {
808+
ASAN_SWITCH(to);
760809
prepare_switch(from, to);
761810
auto _t_ = to->stack.pointer_ref();
762811
register auto f asm("rdx") = from->stack.pointer_ref();
@@ -772,6 +821,7 @@ R"(
772821

773822
inline void switch_context_defer(thread* from, thread* to,
774823
void (*defer)(void*), void* arg) {
824+
ASAN_SWITCH(to);
775825
prepare_switch(from, to);
776826
auto _t_ = to->stack.pointer_ref();
777827
register auto f asm("r9") = from->stack.pointer_ref();
@@ -820,6 +870,7 @@ R"(
820870

821871
DEF_ASM_FUNC(_photon_thread_stub)
822872
R"(
873+
b _asan_start //; asan_start()
823874
ldp x0, x1, [x29, #0x40] //; load arg, start into x0, x1
824875
str xzr, [x29, #0x40] //; set arg as 0
825876
blr x1 //; start(x0)
@@ -835,6 +886,7 @@ R"(
835886
#endif
836887

837888
inline void switch_context(thread* from, thread* to) {
889+
ASAN_SWITCH(to);
838890
prepare_switch(from, to);
839891
auto _t_ = to->stack.pointer_ref();
840892
register auto f asm("x0") = from->stack.pointer_ref();
@@ -854,6 +906,7 @@ R"(
854906

855907
inline void switch_context_defer(thread* from, thread* to,
856908
void (*defer)(void*), void* arg) {
909+
ASAN_SWITCH(to);
857910
prepare_switch(from, to);
858911
auto _t_ = to->stack.pointer_ref();
859912
register auto f asm("x3") = from->stack.pointer_ref();
@@ -899,6 +952,7 @@ R"(
899952
func = (uint64_t)&spinlock_unlock;
900953
arg = &lock;
901954
}
955+
ASAN_DIE_SWITCH(sw.to);
902956
_photon_switch_context_defer_die(
903957
arg, func, sw.to->stack.pointer_ref());
904958
}
@@ -1943,4 +1997,43 @@ R"(
19431997
photon_thread_alloc = _photon_thread_alloc;
19441998
photon_thread_dealloc = _photon_thread_dealloc;
19451999
}
2000+
2001+
extern "C" {
2002+
[[gnu::used]]
2003+
void *gdb_get_thread_stack_ptr(void *th) {
2004+
if (!th)
2005+
return nullptr;
2006+
return ((thread *)th)->stack._ptr;
2007+
}
2008+
[[gnu::used]]
2009+
void *gdb_get_current_thread() {
2010+
return CURRENT;
2011+
}
2012+
[[gnu::used]]
2013+
void *gdb_get_next_thread(void *c) {
2014+
if (!c)
2015+
return nullptr;
2016+
return ((thread *)c)->next();
2017+
}
2018+
[[gnu::used]]
2019+
void *gdb_get_vcpu(void *th) {
2020+
if (!th)
2021+
return nullptr;
2022+
return (void *)((thread *)th)->vcpu;
2023+
}
2024+
[[gnu::used]]
2025+
size_t gdb_get_sleepq_size(void *vcpu) {
2026+
if (!vcpu)
2027+
return 0;
2028+
return ((vcpu_t *)vcpu)->sleepq.q.size();
2029+
}
2030+
[[gnu::used]]
2031+
void *gdb_get_sleepq_item(void *vcpu, size_t idx) {
2032+
if (!vcpu)
2033+
return nullptr;
2034+
if (((vcpu_t *)vcpu)->sleepq.q.size() <= idx)
2035+
return nullptr;
2036+
return ((vcpu_t *)vcpu)->sleepq.q[idx];
2037+
}
2038+
}
19462039
}

tools/photongdb.py

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -52,28 +52,25 @@ def cprint(stat, *args):
5252

5353

5454
def get_next_ready(p):
55-
return gdb.parse_and_eval("(photon::thread*)%s" % p.dereference()['__next_ptr'])
55+
return gdb.parse_and_eval("gdb_get_next_thread((void*){})".format(p))
5656

5757

5858
def get_current():
59-
return gdb.parse_and_eval("(photon::thread*)photon::CURRENT")
59+
return gdb.parse_and_eval("gdb_get_current_thread()")
6060

6161

6262
def get_vcpu(p):
63-
return p.dereference()['vcpu'].dereference()
64-
65-
66-
def get_sleepq(vcpu):
67-
return vcpu['sleepq']['q']
63+
return gdb.parse_and_eval("gdb_get_vcpu((void*){})".format(p))
6864

65+
def get_thread_stack_ptr(p):
66+
return gdb.parse_and_eval("gdb_get_thread_stack_ptr((void*){})".format(p))
6967

7068
def in_sleep(q):
71-
size = q['_M_impl']['_M_finish'] - q['_M_impl']['_M_start']
72-
return [(q['_M_impl']['_M_start'][i]) for i in range(size)]
69+
size = int(gdb.parse_and_eval("(size_t)gdb_get_sleepq_size((void*){})".format(q)))
70+
return [gdb.parse_and_eval("(void*)gdb_get_sleepq_item((void*){}, {})".format(q, i)) for i in range(size)]
7371

7472

7573
def switch_to_ph(regs, rsp, rbp, rip):
76-
cprint('SWITCH', "to {} {} {}".format(hex(rsp), hex(rbp), hex(rip)))
7774
gdb.parse_and_eval("{}={}".format(regs['sp'], rsp))
7875
gdb.parse_and_eval("{}={}".format(regs['bp'], rbp))
7976
gdb.parse_and_eval("{}={}".format(regs['ip'], rip))
@@ -96,7 +93,7 @@ def set_u64_reg(l, r):
9693

9794

9895
def get_stkregs(p):
99-
t = get_u64_ptr(p['stack']['_ptr'])
96+
t = get_u64_ptr(get_thread_stack_ptr(p))
10097
rsp = t + 8
10198
rip = get_u64_val(t + 8)
10299
rbp = get_u64_val(t)
@@ -119,7 +116,7 @@ def load_photon_threads():
119116
photon.append(('READY', p, rsp, rbp, rip))
120117
p = get_next_ready(p)
121118
vcpu = get_vcpu(c)
122-
for t in in_sleep(get_sleepq(vcpu)):
119+
for t in in_sleep(vcpu):
123120
rsp, rbp, rip = get_stkregs(t)
124121
photon.append(('SLEEP', t, rsp, rbp, rip))
125122
return
@@ -163,6 +160,7 @@ def invoke(self, arg, tty):
163160

164161
arch = get_arch()
165162
regs = get_regs(arch)
163+
cprint('SWITCH', "to {} {} {}".format(hex(photon[i][2]), hex(photon[i][3]), hex(photon[i][4])))
166164
switch_to_ph(regs, photon[i][2], photon[i][3], photon[i][4])
167165

168166

@@ -225,12 +223,34 @@ def invoke(self, arg, tty):
225223
enabling = False
226224
cprint('WARNING', "Finished photon thread lookup mode.")
227225

226+
from threading import Lock
227+
228+
class PhotonPs(gdb.Command):
229+
def __init__(self):
230+
gdb.Command.__init__(self, "photon_ps",
231+
gdb.COMMAND_STACK, gdb.COMPLETE_NONE)
232+
self.lock = Lock()
233+
234+
def invoke(self, arg, tty):
235+
with self.lock:
236+
photon_init()
237+
if len(photon) > 0:
238+
for i, (stat, pth, rsp, rbp, rbi) in enumerate(photon):
239+
cprint(
240+
stat, '[{}]'.format(i), pth, hex(rsp), hex(rbp), hex(rbi))
241+
arch = get_arch()
242+
regs = get_regs(arch)
243+
switch_to_ph(regs, rsp, rbp, rbi)
244+
gdb.execute("bt")
245+
switch_to_ph(regs, photon[0][2], photon[0][3], photon[0][4])
246+
photon_restore()
228247

229248
PhotonInit()
230249
PhotonFini()
231250
PhotonRestore()
232251
PhotonThreads()
233252
PhotonLs()
234253
PhotonFr()
254+
PhotonPs()
235255

236256
cprint('INFO', 'Photon-GDB-extension loaded')

tools/ppstack

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
#!/usr/bin/sh
2+
3+
if test $# -ne 1; then
4+
echo "Usage: `basename $0 .sh` <process-id>" 1>&2
5+
exit 1
6+
fi
7+
8+
if test ! -r /proc/$1; then
9+
echo "Process $1 not found." 1>&2
10+
exit 1
11+
fi
12+
13+
# GDB doesn't allow "thread apply all bt" when the process isn't
14+
# threaded; need to peek at the process to determine if that or the
15+
# simpler "bt" should be used.
16+
17+
backtrace="photon_ps"
18+
if test -d /proc/$1/task ; then
19+
# Newer kernel; has a task/ directory.
20+
if test `/bin/ls /proc/$1/task | /usr/bin/wc -l` -gt 1 2>/dev/null ; then
21+
backtrace="thread apply all photon_ps"
22+
fi
23+
elif test -f /proc/$1/maps ; then
24+
# Older kernel; go by it loading libpthread.
25+
if /bin/grep -e libpthread /proc/$1/maps > /dev/null 2>&1 ; then
26+
backtrace="thread apply all photon_ps"
27+
fi
28+
fi
29+
30+
GDB=${GDB:-gdb}
31+
PHOTONDB=${PHOTONDB:-/usr/local/lib/photon/tools/photongdb.py}
32+
PHOTONSO=${PHOTONSO:-/usr/local/lib/libphoton.so}
33+
34+
# Run GDB, strip out unwanted noise.
35+
# --readnever is no longer used since .gdb_index is now in use.
36+
$GDB --quiet -nx $GDBARGS /proc/$1/exe $1 <<EOF 2>&1 |
37+
set width 0
38+
set height 0
39+
set pagination no
40+
source $PHOTONDB
41+
add-symbol-file $PHOTONSO
42+
$backtrace
43+
EOF
44+
/bin/sed -n \
45+
-e 's/^\((gdb) \)*//' \
46+
-e '/^#/p' \
47+
-e '/^Thread/p' \
48+
-e '/CURRENT/p' \
49+
-e '/READY/p' \
50+
-e '/SLEEP/p'

0 commit comments

Comments
 (0)