-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy paththread_name_printer.py
162 lines (133 loc) · 7.16 KB
/
thread_name_printer.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
###
# Copyright 2023-present MongoDB, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
###
"""Pretty-printer for the mongo::(anonymous namespace)::ThreadNameInfo type."""
import typing
import gdb
from gdbmongo.decorable_printer import DecorationIterator
from gdbmongo.libstdcxxutil import shared_ptr_get
from gdbmongo.printer_protocol import SupportsDisplayHint
from gdbmongo.string_data_printer import (StdStringPrinter, StringDataPrinter,
ValueAsPythonStringMixin)
def get_thread_name() -> str:
"""Return the full name associated with the selected thread.
mongo::(anonymous namespace)::ThreadNameInfo is stored a thread-local variable to record the
thread's full name for use by MongoDB's logging subsystem and GDB. This data is useful in the
context of GDB because thread names are otherwise limited to 16 bytes by the Linux kernel.
Furthermore, core dumps do not record the thread names which were made visible to the kernel.
mongo::(anonymous namespace)::ThreadNameInfo is therefore the only memory location where the
thread's name is recorded in a core dump.
"""
try:
# The ThreadNameInfo type replaced the ThreadNameSconce type for representing the storage
# for the thread name as part of SERVER-63852 in MongoDB 6.1 and was then subsequently
# backported to 5.0.12 and 6.0.2. In some versions the ThreadNameInfo instance is managed
# directly as a thread-local variable, and in other versions it is managed as a decoration
# within the ThreadContext object.
thread_info_type = gdb.lookup_type("mongo::(anonymous namespace)::ThreadNameInfo")
except gdb.error as err:
if not err.args[0].startswith("No type named "):
raise
try:
# The ThreadNameSconce type was introduced for representing the storage for the thread
# name as part of SERVER-52821 in MongoDB 5.0. It is always managed as a decoration
# within the ThreadContext object. Previously in MongoDB 4.4, the thread name was made
# available through a thread-local StringData variable.
thread_sconce_type = gdb.lookup_type("mongo::(anonymous namespace)::ThreadNameSconce")
except gdb.error as err2:
if not err2.args[0].startswith("No type named "):
raise
thread_name = gdb.parse_and_eval("mongo::for_debuggers::threadName")
return StringDataPrinter(thread_name).string()
else:
return _get_thread_name_from_sconce(thread_sconce_type)
try:
# The `tls` thread-local variable was introduced to the ThreadNameInfo::forThisThread()
# static function as part of SERVER-66385 in MongoDB 6.1. The ThreadNameInfo instance was
# previously managed as a decoration within the ThreadContext object.
thread_info_pp = gdb.parse_and_eval(
"&'mongo::(anonymous namespace)::ThreadNameInfo::forThisThread()::tls'")
except gdb.error as err:
if not err.args[0].startswith("No symbol "):
raise
if (thread_context := _get_thread_context()) == 0:
return ""
for decoration in DecorationIterator(thread_context.dereference()):
if decoration.type == thread_info_type:
thread_info = decoration.address
break
else:
raise ValueError(
"Failed to locate ThreadNameInfo decoration in ThreadContext") from None
else:
# The 'mongo::(anonymous namespace)::ThreadNameInfo::forThisThread()::Tls' struct has a
# trivial definition and its typeinfo can be elided.
#
# struct Tls {
# ThreadNameInfo* info = new ThreadNameInfo;
# };
#
# (gdb) print 'mongo::(anonymous namespace)::ThreadNameInfo::forThisThread()::tls'
# 'mongo::(anonymous namespace)::ThreadNameInfo::forThisThread()::tls' has unknown type;
# cast it to its declared type
#
# We therefore cast what would be the Tls* to be a ThreadNameInfo** because the Tls::info
# member variable is guaranteed to be stored at byte offset 0 of the struct.
thread_info = thread_info_pp.cast(thread_info_type.pointer().pointer()).dereference()
return _ThreadNameInfoPrinter(thread_info.dereference()).string() if thread_info != 0 else ""
def _get_thread_name_from_sconce(thread_sconce_type: gdb.Type, /) -> str:
"""Return the thread name stored within the ThreadNameSconce instance."""
if (thread_context := _get_thread_context()) == 0:
return ""
for decoration in DecorationIterator(thread_context.dereference()):
if decoration.type == thread_sconce_type:
thread_sconce = decoration.address
break
else:
raise ValueError("Failed to locate ThreadNameSconce decoration in ThreadContext")
if (thread_name := thread_sconce["activePtr"]["px"]) == 0:
thread_name = thread_sconce["cachedPtr"]["px"]
return _ThreadNamePrinter(thread_name.dereference()).string() if thread_name != 0 else ""
def _get_thread_context() -> gdb.Value:
"""Return the ThreadContext object associated with the current thread."""
thread_context_handle_p = gdb.parse_and_eval("&mongo::ThreadContext::_handle")
thread_context_handle_type = gdb.lookup_type("mongo::ThreadContext::Handle")
thread_context_handle_p = thread_context_handle_p.cast(thread_context_handle_type.pointer())
return thread_context_handle_p.dereference()["instance"]["px"]
# mongo::(anonymous namespace)::ThreadNameInfo isn't a type which is likely to be printed so we
# don't bother registering it with GDB.
class _ThreadNameInfoPrinter(SupportsDisplayHint, ValueAsPythonStringMixin):
# pylint: disable=missing-function-docstring
"""Pretty-printer for mongo::(anonymous namespace)::ThreadNameInfo."""
def __init__(self, val: gdb.Value, /) -> None:
self.val = val
@staticmethod
def display_hint() -> typing.Literal["string"]:
return "string"
def to_string(self) -> str:
thread_name = shared_ptr_get(self.val["_h"]["_ptr"]).dereference()
return StdStringPrinter(thread_name).string()
# mongo::ThreadName isn't a type which is likely to be printed so we don't bother registering it
# with GDB.
class _ThreadNamePrinter(SupportsDisplayHint, ValueAsPythonStringMixin):
# pylint: disable=missing-function-docstring
"""Pretty-printer for mongo::ThreadName."""
def __init__(self, val: gdb.Value, /) -> None:
self.val = val
@staticmethod
def display_hint() -> typing.Literal["string"]:
return "string"
def to_string(self) -> str:
return StdStringPrinter(self.val["_storage"]).string()