[lldb-vscode] Fix focus thread when previous thread exits

The thread that Visual Studio Code displays on a stop is called the focus thread. When the previous focus thread exits and we stop in a new thread, lldb-vscode does not tell vscode to set the new thread as the focus thread, so it selects the first thread in the thread list.

This patch changes lldb-vscode to tell vscode that the new thread is the focus thread. It also includes a test that verifies the DAP stop message for this case contains the correct values.

Reviewed By: clayborg, wallace

Differential Revision: https://reviews.llvm.org/D109633
This commit is contained in:
Ted Woodward 2021-09-15 18:03:42 -05:00
parent 37a5a3ae55
commit 17589538aa
4 changed files with 84 additions and 6 deletions

View File

@ -0,0 +1,4 @@
C_SOURCES := main.c
CFLAGS_EXTRAS := -lpthread
include Makefile.rules

View File

@ -0,0 +1,47 @@
"""
Test lldb-vscode setBreakpoints request
"""
from __future__ import print_function
import unittest2
import vscode
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
from lldbsuite.test import lldbutil
import lldbvscode_testcase
class TestVSCode_correct_thread(lldbvscode_testcase.VSCodeTestCaseBase):
mydir = TestBase.compute_mydir(__file__)
@skipIfWindows
@skipIfRemote
def test_correct_thread(self):
'''
Tests that the correct thread is selected if we continue from
a thread that goes away and hit a breakpoint in another thread.
In this case, the selected thread should be the thread that
just hit the breakpoint, and not the first thread in the list.
'''
program = self.getBuildArtifact("a.out")
self.build_and_launch(program)
source = 'main.c'
breakpoint_line = line_number(source, '// break here')
lines = [breakpoint_line]
# Set breakpoint in the thread function
breakpoint_ids = self.set_source_breakpoints(source, lines)
self.assertEqual(len(breakpoint_ids), len(lines),
"expect correct number of breakpoints")
self.continue_to_breakpoints(breakpoint_ids)
# We're now stopped at the breakpoint in the first thread, thread #2.
# Continue to join the first thread and hit the breakpoint in the
# second thread, thread #3.
self.vscode.request_continue()
stopped_event = self.vscode.wait_for_stopped()
# Verify that the description is the relevant breakpoint,
# preserveFocusHint is False and threadCausedFocus is True
self.assertTrue(stopped_event[0]['body']['description'].startswith('breakpoint %s.' % breakpoint_ids[0]))
self.assertFalse(stopped_event[0]['body']['preserveFocusHint'])
self.assertTrue(stopped_event[0]['body']['threadCausedFocus'])

View File

@ -0,0 +1,23 @@
#include <pthread.h>
#include <stdio.h>
int state_var;
void *thread (void *in)
{
state_var++; // break here
return NULL;
}
int main(int argc, char **argv)
{
pthread_t t1, t2;
pthread_create(&t1, NULL, *thread, NULL);
pthread_join(t1, NULL);
pthread_create(&t2, NULL, *thread, NULL);
pthread_join(t2, NULL);
printf("state_var is %d\n", state_var);
return 0;
}

View File

@ -232,13 +232,17 @@ void SendThreadStoppedEvent() {
// set it as the focus thread if below if needed.
lldb::tid_t first_tid_with_reason = LLDB_INVALID_THREAD_ID;
uint32_t num_threads_with_reason = 0;
bool focus_thread_exists = false;
for (uint32_t thread_idx = 0; thread_idx < num_threads; ++thread_idx) {
lldb::SBThread thread = process.GetThreadAtIndex(thread_idx);
const lldb::tid_t tid = thread.GetThreadID();
const bool has_reason = ThreadHasStopReason(thread);
// If the focus thread doesn't have a stop reason, clear the thread ID
if (tid == g_vsc.focus_tid && !has_reason)
g_vsc.focus_tid = LLDB_INVALID_THREAD_ID;
if (tid == g_vsc.focus_tid) {
focus_thread_exists = true;
if (!has_reason)
g_vsc.focus_tid = LLDB_INVALID_THREAD_ID;
}
if (has_reason) {
++num_threads_with_reason;
if (first_tid_with_reason == LLDB_INVALID_THREAD_ID)
@ -246,10 +250,10 @@ void SendThreadStoppedEvent() {
}
}
// We will have cleared g_vsc.focus_tid if he focus thread doesn't
// have a stop reason, so if it was cleared, or wasn't set, then set the
// focus thread to the first thread with a stop reason.
if (g_vsc.focus_tid == LLDB_INVALID_THREAD_ID)
// We will have cleared g_vsc.focus_tid if he focus thread doesn't have
// a stop reason, so if it was cleared, or wasn't set, or doesn't exist,
// then set the focus thread to the first thread with a stop reason.
if (!focus_thread_exists || g_vsc.focus_tid == LLDB_INVALID_THREAD_ID)
g_vsc.focus_tid = first_tid_with_reason;
// If no threads stopped with a reason, then report the first one so