Skip to content

Commit 4365302

Browse files
authored
Add thread name back in (#21791)
1 parent d0095c7 commit 4365302

File tree

24 files changed

+704
-192
lines changed

24 files changed

+704
-192
lines changed

arangod/SystemMonitor/AsyncRegistry/PrettyPrinter/src/asyncregistry/gdb_data.py

Lines changed: 36 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from __future__ import annotations
22
from typing import Iterable
33
from dataclasses import dataclass
4+
import gdb
45

56
@dataclass
67
class State:
@@ -23,7 +24,6 @@ def __str__(self):
2324
class Thread:
2425
posix_id: gdb.Value
2526
lwpid: gdb.Value
26-
# TODO is there a way to get the thread name?
2727

2828
@classmethod
2929
def from_gdb(cls, value: gdb.Value | None):
@@ -34,6 +34,18 @@ def from_gdb(cls, value: gdb.Value | None):
3434
def __str__(self):
3535
return f"LWPID {self.lwpid} (pthread {self.posix_id})"
3636

37+
@dataclass
38+
class ThreadInfo:
39+
lwpid: gdb.Value
40+
name: gdb.Value
41+
42+
@classmethod
43+
def from_gdb(cls, value: gdb.Value):
44+
return cls(value['kernel_id'], value['name'])
45+
46+
def __str__(self):
47+
return f"{self.name} (LWPID {self.lwpid})"
48+
3749
@dataclass
3850
class SourceLocation:
3951
file_name: gdb.Value
@@ -49,18 +61,26 @@ def __str__(self):
4961

5062
@dataclass
5163
class Requester:
52-
content: Thread | PromiseId
64+
content: ThreadInfo | PromiseId
5365

5466
@classmethod
5567
def from_gdb(cls, value: gdb.Value):
56-
alternative = value['_M_index']
57-
if alternative == 0:
58-
content = Thread.from_gdb(value['_M_u']['_M_first']['_M_storage'])
59-
elif alternative == 1:
60-
content = PromiseId(value['_M_u']['_M_rest']['_M_first']['_M_storage'])
61-
else:
62-
return "wrong input"
63-
return cls(content)
68+
num_flag_bits = 1
69+
flag_mask = (1 << num_flag_bits) - 1
70+
BIT_WIDTH_64 = 64
71+
all_ones_64_bit_mask = (1 << BIT_WIDTH_64) - 1
72+
data_mask_64bit = flag_mask ^ all_ones_64_bit_mask
73+
if value & flag_mask:
74+
return cls(ThreadInfo.from_gdb(
75+
(value & data_mask_64bit)
76+
.cast(gdb.lookup_type("arangodb::containers::SharedResource<arangodb::basics::ThreadInfo>").pointer())
77+
.dereference()['_data']
78+
))
79+
else:
80+
return cls(PromiseId(
81+
(value & data_mask_64bit)
82+
.cast(gdb.lookup_type("void").pointer())
83+
))
6484

6585
def __str__(self):
6686
return str(self.content)
@@ -81,7 +101,8 @@ def __str__(self):
81101
@dataclass
82102
class Promise:
83103
id: PromiseId
84-
thread: Optional[Thread]
104+
owning_thread: ThreadInfo
105+
running_thread: Optional[Thread]
85106
source_location: SourceLocation
86107
requester: Requester
87108
state: State
@@ -90,18 +111,19 @@ class Promise:
90111
def from_gdb(cls, ptr: gdb.Value, value: gdb.Value):
91112
return cls(
92113
PromiseId(ptr),
114+
ThreadInfo.from_gdb(value["owning_thread"]["_resource"].dereference()["_data"]),
93115
Thread.from_gdb(GdbOptional.from_gdb(value["running_thread"])._value),
94116
SourceLocation.from_gdb(value["source_location"]),
95-
Requester.from_gdb(value["requester"]["_M_i"]),
117+
Requester.from_gdb(value["requester"]["_resource"]["_M_i"]),
96118
State.from_gdb(value["state"])
97119
)
98120

99121
def is_valid(self):
100122
return not self.state.is_deleted()
101123

102124
def __str__(self):
103-
thread_str = f" on {self.thread}" if self.thread else ""
104-
return str(self.source_location) + ", " + str(self.state) + thread_str
125+
thread_str = f" on {self.running_thread}" if self.running_thread else ""
126+
return str(self.source_location) + ", owned by " + str(self.owning_thread) + ", " + str(self.state) + thread_str
105127

106128
@dataclass
107129
class GdbOptional:

arangod/SystemMonitor/AsyncRegistry/PrettyPrinter/src/asyncregistry/gdb_forest.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from typing import Iterable, Any
2-
from asyncregistry.gdb_data import Promise, PromiseId, Thread
2+
from asyncregistry.gdb_data import Promise, PromiseId, ThreadInfo
33

44
Id = str
55
class Forest:
@@ -31,7 +31,7 @@ def children(self, id: Id) -> Iterable[Id]:
3131
yield child
3232

3333
@classmethod
34-
def from_promises(cls, promises: Iterable[tuple[PromiseId, PromiseId, Any]]):
34+
def from_promises(cls, promises: Iterable[tuple[PromiseId, ThreadInfo | PromiseId, Any]]):
3535
parents: [Id] = [] # all parents promise ids
3636
nodes: [Any] = [] # all nodes
3737
positions: {Id: int} = {} # lookup table for parent and node per promise id
@@ -42,7 +42,7 @@ def from_promises(cls, promises: Iterable[tuple[PromiseId, PromiseId, Any]]):
4242
promise_id = str(promise.id)
4343
positions[promise_id] = count
4444
nodes.append(data)
45-
if type(parent) == Thread:
45+
if type(parent) == ThreadInfo:
4646
parents.append(None)
4747
roots.append(promise_id)
4848
else:

arangod/SystemMonitor/AsyncRegistry/PrettyPrinter/src/pretty-printer.py

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,16 @@ def from_json(cls, blob: dict):
2424
def __str__(self):
2525
return f"LWPID {self.id} (pthread {self.posix_id})"
2626

27+
class ThreadInfo(object):
28+
def __init__(self, lwpid: int, name: str):
29+
self.lwpid = lwpid
30+
self.name = name
31+
@classmethod
32+
def from_json(cls, blob: dict):
33+
return cls(blob["LWPID"], blob["name"])
34+
def __str__(self):
35+
return f"{self.name} (LWPID {self.lwpid})"
36+
2737
class SourceLocation(object):
2838
def __init__(self, file_name: str, line: int, function_name: str):
2939
self.file_name = file_name
@@ -34,43 +44,52 @@ def from_json(cls, blob: dict):
3444
return cls(blob["file_name"], blob["line"], blob["function_name"])
3545
def __str__(self):
3646
return self.function_name + " (" + self.file_name + ":" + str(self.line) + ")"
47+
48+
class PromiseId(object):
49+
def __init__(self, id):
50+
self.id = id
51+
@classmethod
52+
def from_json(cls, blob: dict):
53+
return cls(blob["id"])
54+
def __str__(self):
55+
return self.id
3756

3857
class Requester(object):
39-
def __init__(self, is_sync: bool, item: int):
58+
def __init__(self, is_sync: bool, item: ThreadInfo | PromiseId):
4059
self.is_sync = is_sync
4160
self.item = item
4261
@classmethod
4362
def from_json(cls, blob: dict):
4463
if not blob:
4564
return None
46-
sync = blob.get("thread")
47-
if sync is not None:
48-
return cls(True, sync)
65+
if "LWPID" in blob:
66+
return cls(True, ThreadInfo.from_json(blob))
4967
else:
50-
return cls(False, blob["promise"])
68+
return cls(False, PromiseId.from_json(blob))
5169
def __str__(self):
5270
if self.is_sync:
5371
# a sync requester is always at the bottom of a tree,
5472
# but has no entry on its own,
5573
# therefore just add it here in a new line
56-
return "\n" + "─ " + str(Thread.from_json(self.item))
74+
return "\n" + "─ " + str(self.item)
5775
else:
5876
return ""
5977

6078
class Data(object):
61-
def __init__(self, running_thread: Optional[Thread], source_location: SourceLocation, id: int, state: str, requester: Requester):
79+
def __init__(self, owning_thread: ThreadInfo, running_thread: Optional[Thread], source_location: SourceLocation, id: int, state: str, requester: Requester):
80+
self.owning_thread = owning_thread
6281
self.running_thread = running_thread
6382
self.source_location = source_location
6483
self.id = id
6584
self.waiter = requester
6685
self.state = state
6786
@classmethod
6887
def from_json(cls, blob: dict):
69-
return cls(Thread.from_json(blob["running_thread"]) if "running_thread" in blob else None, SourceLocation.from_json(blob["source_location"]), blob["id"], blob["state"], Requester.from_json(blob["requester"]))
88+
return cls(ThreadInfo.from_json(blob["owning_thread"]), Thread.from_json(blob["running_thread"]) if "running_thread" in blob else None, SourceLocation.from_json(blob["source_location"]), blob["id"], blob["state"], Requester.from_json(blob["requester"]))
7089
def __str__(self):
7190
waiter_str = str(self.waiter) if self.waiter != None else ""
7291
thread_str = f" on {self.running_thread}" if self.running_thread else ""
73-
return str(self.source_location) + ", " + self.state + thread_str + waiter_str
92+
return str(self.source_location) + ", owned by " + str(self.owning_thread) + ", " + self.state + thread_str + waiter_str
7493

7594
class Promise(object):
7695
def __init__(self, hierarchy: int, data: Data):

arangod/SystemMonitor/AsyncRegistry/RestHandler.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -72,12 +72,12 @@ auto all_undeleted_promises() -> ForestWithRoots<PromiseSnapshot> {
7272
registry.for_node([&](PromiseSnapshot promise) {
7373
if (promise.state != State::Deleted) {
7474
std::visit(overloaded{
75-
[&](PromiseId async_waiter) {
76-
forest.insert(promise.id, async_waiter, promise);
75+
[&](PromiseId const& async_waiter) {
76+
forest.insert(promise.id.id, async_waiter.id, promise);
7777
},
78-
[&](basics::ThreadId sync_waiter_thread) {
79-
forest.insert(promise.id, nullptr, promise);
80-
roots.emplace_back(promise.id);
78+
[&](basics::ThreadInfo const& sync_waiter_thread) {
79+
forest.insert(promise.id.id, nullptr, promise);
80+
roots.emplace_back(promise.id.id);
8181
},
8282
},
8383
promise.requester);

lib/Async/Registry/promise.cpp

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -28,17 +28,21 @@
2828

2929
using namespace arangodb::async_registry;
3030

31-
Promise::Promise(Requester requester, std::source_location entry_point)
32-
: owning_thread{basics::ThreadId::current()},
33-
requester{requester},
31+
Promise::Promise(CurrentRequester requester, std::source_location entry_point)
32+
: owning_thread{basics::ThreadInfo::current()},
33+
requester{
34+
AtomicRequester::from(requester)}, // TODO hand this in via function
3435
state{State::Running},
3536
running_thread{basics::ThreadId::current()},
3637
source_location{entry_point.file_name(), entry_point.function_name(),
3738
entry_point.line()} {}
3839

39-
auto arangodb::async_registry::get_current_coroutine() noexcept -> Requester* {
40+
// TODO return either SharedPtr<ThreadInfo> or *void but does not need to be
41+
// atomic
42+
auto arangodb::async_registry::get_current_coroutine() noexcept
43+
-> CurrentRequester* {
4044
struct Guard {
41-
Requester identifier = Requester::current_thread();
45+
CurrentRequester identifier = {basics::ThreadInfo::current()};
4246
};
4347
// make sure that this is only created once on a thread
4448
static thread_local auto current = Guard{};
@@ -55,16 +59,22 @@ AddToAsyncRegistry::~AddToAsyncRegistry() {
5559
node_in_registry->list->mark_for_deletion(node_in_registry.get());
5660
}
5761
}
58-
auto AddToAsyncRegistry::update_requester(Requester new_requester) -> void {
62+
auto AddToAsyncRegistry::update_requester(std::optional<PromiseId> requester)
63+
-> void {
64+
if (node_in_registry != nullptr && requester.has_value()) {
65+
node_in_registry->data.requester.store(requester.value().id);
66+
}
67+
}
68+
auto AddToAsyncRegistry::update_requester_to_current_thread() -> void {
5969
if (node_in_registry != nullptr) {
60-
node_in_registry->data.requester.store(new_requester);
70+
node_in_registry->data.requester.store(basics::ThreadInfo::current());
6171
}
6272
}
63-
auto AddToAsyncRegistry::id() -> void* {
73+
auto AddToAsyncRegistry::id() -> std::optional<PromiseId> {
6474
if (node_in_registry != nullptr) {
65-
return node_in_registry->data.id();
75+
return {node_in_registry->data.id()};
6676
} else {
67-
return nullptr;
77+
return std::nullopt;
6878
}
6979
}
7080
auto AddToAsyncRegistry::update_source_location(std::source_location loc)

0 commit comments

Comments
 (0)