3 from collections
import defaultdict
11 Adapted from networkx: http://networkx.github.io/ 12 Represents a directed graph. Edges can store (key, value) attributes. 23 return self.adjacency_map.get(node,
set())
27 for node, neighbors
in self.adjacency_map.items():
28 for neighbor
in neighbors:
29 edges.append((node, neighbor))
33 return self.adjacency_map.keys()
47 self.adjacency_map.pop(node,
None)
48 for _, neighbors
in self.adjacency_map.items():
49 neighbors.discard(node)
56 graph.add_edge(node, neighbor)
61 Returns the graph as a dictionary in a format that can be 74 for node
in self.adjacency_map.keys():
75 node_to_number[node] = len(data[
'nodes'])
76 data[
'nodes'].
append({
'id': node})
79 for node, neighbors
in self.adjacency_map.items():
80 for neighbor
in neighbors:
82 link[
'source'] = node_to_number[node]
83 link[
'target'] = node_to_number[neighbor]
90 Adapted from networkx: http://networkx.github.io/ 96 comp : generator of sets 97 A generator of sets of nodes, one for each strongly connected 105 for source
in G.nodes():
106 if source
not in scc_found:
110 if v
not in preorder:
114 v_nbrs = G.neighbors(v)
116 if w
not in preorder:
121 lowlink[v] = preorder[v]
123 if w
not in scc_found:
124 if preorder[w] > preorder[v]:
125 lowlink[v] =
min([lowlink[v], lowlink[w]])
127 lowlink[v] =
min([lowlink[v], preorder[w]])
129 if lowlink[v] == preorder[v]:
133 scc_queue
and preorder[scc_queue[-1]] > preorder[v]
145 Adapted from networkx: http://networkx.github.io/ 151 cycle_generator: generator 152 A generator that produces elementary cycles of the graph. 153 Each cycle is represented by a list of nodes along the cycle. 156 def _unblock(thisnode, blocked, B):
157 stack =
set([thisnode])
162 stack.update(B[node])
171 subG = G.subgraph(G.nodes())
176 startnode = scc.pop()
181 blocked.add(startnode)
183 stack = [(startnode,
list(subG.neighbors(startnode)))]
185 thisnode, nbrs = stack[-1]
187 nextnode = nbrs.pop()
188 if nextnode == startnode:
191 elif nextnode
not in blocked:
192 path.append(nextnode)
193 stack.append((nextnode,
list(subG.neighbors(nextnode))))
194 closed.discard(nextnode)
195 blocked.add(nextnode)
199 if thisnode
in closed:
200 _unblock(thisnode, blocked, B)
202 for nbr
in subG.neighbors(thisnode):
203 if thisnode
not in B[nbr]:
208 subG.remove_node(startnode)
209 H = subG.subgraph(scc)
215 Looks for a cycle in the graph. If found, returns the first cycle. 216 If nodes a1, a2, ..., an are in a cycle, then this returns: 217 [(a1,a2), (a2,a3), ... (an-1,an), (an, a1)] 218 Otherwise returns an empty list. 223 nodes.append(nodes[0])
226 for node
in nodes[1:]:
227 edges.append((prev, node))
236 Returns the stack trace for the thread id as a list of strings. 238 gdb.execute(
'thread %d' % thread_id, from_tty=
False, to_string=
True)
239 output = gdb.execute(
'bt', from_tty=
False, to_string=
True)
240 stacktrace_lines = output.strip().
split(
'\n')
241 return stacktrace_lines
245 thread_id, top_line, expected_top_line, expected_frame
248 Returns True if we found expected_top_line in top_line, and 249 we found the expected_frame in the thread's stack trace. 251 if expected_top_line
not in top_line:
254 return any(expected_frame
in line
for line
in stacktrace_lines)
258 '''Types of mutexes that we can detect deadlocks.''' 260 PTHREAD_MUTEX_T =
'pthread_mutex_t' 261 PTHREAD_RWLOCK_T =
'pthread_rwlock_t' 266 Returns the probable mutex type, based on the first line 267 of the thread's stack. Returns None if not found. 271 thread_id, top_line,
'__lll_lock_wait',
'pthread_mutex' 273 return MutexType.PTHREAD_MUTEX_T
275 thread_id, top_line,
'futex_wait',
'pthread_rwlock' 277 return MutexType.PTHREAD_RWLOCK_T
283 Returns a function to resolve the mutex owner and address for 284 the given type. The returned function f has the following 287 f: args: (map of thread lwp -> thread id), blocked thread lwp 288 returns: (lwp of thread owning mutex, mutex address) 289 or (None, None) if not found. 291 Returns None if there is no function for this mutex_type. 293 if mutex_type == MutexType.PTHREAD_MUTEX_T:
294 return get_pthread_mutex_t_owner_and_address
295 if mutex_type == MutexType.PTHREAD_RWLOCK_T:
296 return get_pthread_rwlock_t_owner_and_address
301 '''Prints the threads and mutexes involved in the deadlock.''' 304 'Thread %d (LWP %d) is waiting on %s (0x%016x) held by ' 305 'Thread %d (LWP %d)' % (
306 lwp_to_thread_id[m], m,
307 graph.attributes(m, n)[
'mutex_type'].value,
308 graph.attributes(m, n)[
'mutex'], lwp_to_thread_id[n], n
316 - map of LWP -> thread ID 317 - map of blocked threads LWP -> potential mutex type 320 lwp_to_thread_id = {}
325 output = gdb.execute(
'info threads', from_tty=
False, to_string=
True)
326 lines = output.strip().
split(
'\n')[1:]
327 regex = re.compile(
r'[\s\*]*(\d+).*Thread.*\(LWP (\d+)\).*')
330 thread_id = int(regex.match(line).
group(1))
331 thread_lwp = int(regex.match(line).
group(2))
332 lwp_to_thread_id[thread_lwp] = thread_id
333 mutex_type = MutexType.get_mutex_type(thread_id, line)
335 blocked_threads[thread_lwp] = mutex_type
339 return (lwp_to_thread_id, blocked_threads)
344 Finds the thread holding the mutex that this thread is blocked on. 345 Returns a pair of (lwp of thread owning mutex, mutex address), 346 or (None, None) if not found. 350 'thread %d' % lwp_to_thread_id[thread_lwp],
354 gdb.execute(
'frame 1', from_tty=
False, to_string=
True)
359 mutex_info = gdb.parse_and_eval(
'mutex').
dereference()
360 mutex_owner_lwp = int(mutex_info[
'__data'][
'__owner'])
361 return (mutex_owner_lwp, int(mutex_info.address))
368 If the thread is waiting on a write-locked pthread_rwlock_t, this will 370 (lwp of thread that is write-owning the mutex, mutex address) 371 or (None, None) if not found, or if the mutex is read-locked. 375 'thread %d' % lwp_to_thread_id[thread_lwp],
379 gdb.execute(
'frame 2', from_tty=
False, to_string=
True)
384 rwlock_info = gdb.parse_and_eval(
'rwlock').
dereference()
385 rwlock_owner_lwp = int(rwlock_info[
'__data'][
'__writer'])
389 if rwlock_owner_lwp != 0:
390 return (rwlock_owner_lwp, int(rwlock_info.address))
398 '''Detects deadlocks''' 401 super(Deadlock, self).
__init__(
'deadlock', gdb.COMMAND_NONE)
404 '''Prints the threads and mutexes in a deadlock, if it exists.''' 413 for thread_lwp, mutex_type
in blocked_threads.items():
414 get_owner_and_address_func = \
415 MutexType.get_mutex_owner_and_address_func_for_type(mutex_type)
416 if not get_owner_and_address_func:
418 mutex_owner_lwp, mutex_address = get_owner_and_address_func(
419 lwp_to_thread_id, thread_lwp
421 if mutex_owner_lwp
and mutex_address:
426 mutex_type=mutex_type
432 print(
'Found deadlock!')
436 'No deadlock detected. ' 437 'Do you have debug symbols installed?' 444 print(
'Type "deadlock" to detect deadlocks.')
448 return 'Detect deadlocks' 451 if __name__ ==
'__main__':
def get_pthread_rwlock_t_owner_and_address(lwp_to_thread_id, thread_lwp)
def get_mutex_type(thread_id, top_line)
def neighbors(self, node)
void append(std::unique_ptr< IOBuf > &buf, StringPiece str)
def print_cycle(graph, lwp_to_thread_id, cycle)
def attributes(self, node1, node2)
Composed any(Predicate pred=Predicate())
constexpr std::decay< T >::type copy(T &&value) noexcept(noexcept(typename std::decay< T >::type(std::forward< T >(value))))
def invoke(self, arg, from_tty)
def strongly_connected_components(G)
def get_pthread_mutex_t_owner_and_address(lwp_to_thread_id, thread_lwp)
Encoder::MutableCompressedList list
S split(const StringPiece source, char delimiter)
def is_thread_blocked_with_frame(thread_id, top_line, expected_top_line, expected_frame)
def get_mutex_owner_and_address_func_for_type(mutex_type)
def remove_node(self, node)
Optional< NamedGroup > group
def add_edge(self, node1, node2, kwargs)
def subgraph(self, nodes)
def get_stacktrace(thread_id)
constexpr detail::Dereference dereference