Skip to content

Commit f810491

Browse files
committedMar 17, 2016
Fix deadlock due to thread list locking in 'bt all' with obj-c
Summary: The gdb-remote async thread cannot modify thread state while the main thread holds a lock on the state. Don't use locking thread iteration for bt all. Specifically, the deadlock manifests when lldb attempts to JIT code to symbolicate objective c while backtracing. As part of this code path, SetPrivateState() is called on an async thread. This async thread will block waiting for the thread_list lock held by the main thread in CommandObjectIterateOverThreads. The main thread will also block on the async thread during DoResume (although with a timeout), leading to a deadlock. Due to the timeout, the deadlock is not immediately apparent, but the inferior will be left in an invalid state after the bt all completes, and objective-c symbols will not be successfully resolved in the backtrace. Reviewers: andrew.w.kaylor, jingham, clayborg Subscribers: sas, lldb-commits Differential Revision: http://reviews.llvm.org/D18075 Change by Francis Ricci <fjricci@fb.com> llvm-svn: 263735
1 parent 7b390ec commit f810491

File tree

1 file changed

+72
-38
lines changed

1 file changed

+72
-38
lines changed
 

Diff for: ‎lldb/source/Commands/CommandObjectThread.cpp

+72-38
Original file line numberDiff line numberDiff line change
@@ -66,61 +66,65 @@ class CommandObjectIterateOverThreads : public CommandObjectParsed
6666
if (command.GetArgumentCount() == 0)
6767
{
6868
Thread *thread = m_exe_ctx.GetThreadPtr();
69-
if (!HandleOneThread (*thread, result))
69+
if (!HandleOneThread (thread->GetID(), result))
7070
return false;
71+
return result.Succeeded();
7172
}
72-
else if (command.GetArgumentCount() == 1 && ::strcmp (command.GetArgumentAtIndex(0), "all") == 0)
73+
74+
// Use tids instead of ThreadSPs to prevent deadlocking problems which result from JIT-ing
75+
// code while iterating over the (locked) ThreadSP list.
76+
std::vector<lldb::tid_t> tids;
77+
78+
if (command.GetArgumentCount() == 1 && ::strcmp (command.GetArgumentAtIndex(0), "all") == 0)
7379
{
7480
Process *process = m_exe_ctx.GetProcessPtr();
75-
uint32_t idx = 0;
76-
for (ThreadSP thread_sp : process->Threads())
77-
{
78-
if (idx != 0 && m_add_return)
79-
result.AppendMessage("");
8081

81-
if (!HandleOneThread(*(thread_sp.get()), result))
82-
return false;
83-
++idx;
84-
}
82+
for (ThreadSP thread_sp : process->Threads())
83+
tids.push_back(thread_sp->GetID());
8584
}
8685
else
8786
{
8887
const size_t num_args = command.GetArgumentCount();
8988
Process *process = m_exe_ctx.GetProcessPtr();
89+
9090
Mutex::Locker locker (process->GetThreadList().GetMutex());
91-
std::vector<ThreadSP> thread_sps;
9291

9392
for (size_t i = 0; i < num_args; i++)
9493
{
9594
bool success;
96-
95+
9796
uint32_t thread_idx = StringConvert::ToUInt32(command.GetArgumentAtIndex(i), 0, 0, &success);
9897
if (!success)
9998
{
10099
result.AppendErrorWithFormat ("invalid thread specification: \"%s\"\n", command.GetArgumentAtIndex(i));
101100
result.SetStatus (eReturnStatusFailed);
102101
return false;
103102
}
104-
105-
thread_sps.push_back(process->GetThreadList().FindThreadByIndexID(thread_idx));
106-
107-
if (!thread_sps[i])
103+
104+
ThreadSP thread = process->GetThreadList().FindThreadByIndexID(thread_idx);
105+
106+
if (!thread)
108107
{
109108
result.AppendErrorWithFormat ("no thread with index: \"%s\"\n", command.GetArgumentAtIndex(i));
110109
result.SetStatus (eReturnStatusFailed);
111110
return false;
112111
}
113-
}
114-
115-
for (uint32_t i = 0; i < num_args; i++)
116-
{
117-
if (!HandleOneThread (*(thread_sps[i].get()), result))
118-
return false;
119112

120-
if (i < num_args - 1 && m_add_return)
121-
result.AppendMessage("");
113+
tids.push_back(thread->GetID());
122114
}
123115
}
116+
117+
uint32_t idx = 0;
118+
for (const lldb::tid_t &tid : tids)
119+
{
120+
if (idx != 0 && m_add_return)
121+
result.AppendMessage("");
122+
123+
if (!HandleOneThread (tid, result))
124+
return false;
125+
126+
++idx;
127+
}
124128
return result.Succeeded();
125129
}
126130

@@ -133,7 +137,7 @@ class CommandObjectIterateOverThreads : public CommandObjectParsed
133137
// If m_add_return is true, a blank line will be inserted between each of the listings (except the last one.)
134138

135139
virtual bool
136-
HandleOneThread (Thread &thread, CommandReturnObject &result) = 0;
140+
HandleOneThread (lldb::tid_t, CommandReturnObject &result) = 0;
137141

138142
ReturnStatus m_success_return = eReturnStatusSuccessFinishResult;
139143
bool m_add_return = true;
@@ -275,25 +279,35 @@ class CommandObjectThreadBacktrace : public CommandObjectIterateOverThreads
275279
}
276280

277281
bool
278-
HandleOneThread (Thread &thread, CommandReturnObject &result) override
282+
HandleOneThread (lldb::tid_t tid, CommandReturnObject &result) override
279283
{
284+
ThreadSP thread_sp = m_exe_ctx.GetProcessPtr()->GetThreadList().FindThreadByID(tid);
285+
if (!thread_sp)
286+
{
287+
result.AppendErrorWithFormat ("thread disappeared while computing backtraces: 0x%" PRIx64 "\n", tid);
288+
result.SetStatus (eReturnStatusFailed);
289+
return false;
290+
}
291+
292+
Thread *thread = thread_sp.get();
293+
280294
Stream &strm = result.GetOutputStream();
281295

282296
// Don't show source context when doing backtraces.
283297
const uint32_t num_frames_with_source = 0;
284298

285-
if (!thread.GetStatus (strm,
286-
m_options.m_start,
287-
m_options.m_count,
288-
num_frames_with_source))
299+
if (!thread->GetStatus (strm,
300+
m_options.m_start,
301+
m_options.m_count,
302+
num_frames_with_source))
289303
{
290-
result.AppendErrorWithFormat ("error displaying backtrace for thread: \"0x%4.4x\"\n", thread.GetIndexID());
304+
result.AppendErrorWithFormat ("error displaying backtrace for thread: \"0x%4.4x\"\n", thread->GetIndexID());
291305
result.SetStatus (eReturnStatusFailed);
292306
return false;
293307
}
294308
if (m_options.m_extended_backtrace)
295309
{
296-
DoExtendedBacktrace (&thread, result);
310+
DoExtendedBacktrace (thread, result);
297311
}
298312

299313
return true;
@@ -1537,12 +1551,22 @@ class CommandObjectThreadInfo : public CommandObjectIterateOverThreads
15371551
}
15381552

15391553
bool
1540-
HandleOneThread (Thread &thread, CommandReturnObject &result) override
1554+
HandleOneThread (lldb::tid_t tid, CommandReturnObject &result) override
15411555
{
1556+
ThreadSP thread_sp = m_exe_ctx.GetProcessPtr()->GetThreadList().FindThreadByID(tid);
1557+
if (!thread_sp)
1558+
{
1559+
result.AppendErrorWithFormat ("thread no longer exists: 0x%" PRIx64 "\n", tid);
1560+
result.SetStatus (eReturnStatusFailed);
1561+
return false;
1562+
}
1563+
1564+
Thread *thread = thread_sp.get();
1565+
15421566
Stream &strm = result.GetOutputStream();
1543-
if (!thread.GetDescription (strm, eDescriptionLevelFull, m_options.m_json_thread, m_options.m_json_stopinfo))
1567+
if (!thread->GetDescription (strm, eDescriptionLevelFull, m_options.m_json_thread, m_options.m_json_stopinfo))
15441568
{
1545-
result.AppendErrorWithFormat ("error displaying info for thread: \"%d\"\n", thread.GetIndexID());
1569+
result.AppendErrorWithFormat ("error displaying info for thread: \"%d\"\n", thread->GetIndexID());
15461570
result.SetStatus (eReturnStatusFailed);
15471571
return false;
15481572
}
@@ -2044,14 +2068,24 @@ class CommandObjectThreadPlanList : public CommandObjectIterateOverThreads
20442068

20452069
protected:
20462070
bool
2047-
HandleOneThread (Thread &thread, CommandReturnObject &result) override
2071+
HandleOneThread (lldb::tid_t tid, CommandReturnObject &result) override
20482072
{
2073+
ThreadSP thread_sp = m_exe_ctx.GetProcessPtr()->GetThreadList().FindThreadByID(tid);
2074+
if (!thread_sp)
2075+
{
2076+
result.AppendErrorWithFormat ("thread no longer exists: 0x%" PRIx64 "\n", tid);
2077+
result.SetStatus (eReturnStatusFailed);
2078+
return false;
2079+
}
2080+
2081+
Thread *thread = thread_sp.get();
2082+
20492083
Stream &strm = result.GetOutputStream();
20502084
DescriptionLevel desc_level = eDescriptionLevelFull;
20512085
if (m_options.m_verbose)
20522086
desc_level = eDescriptionLevelVerbose;
20532087

2054-
thread.DumpThreadPlans (&strm, desc_level, m_options.m_internal, true);
2088+
thread->DumpThreadPlans (&strm, desc_level, m_options.m_internal, true);
20552089
return true;
20562090
}
20572091

0 commit comments

Comments
 (0)
Please sign in to comment.