[Support] [lldb] Fix thread jump #45326 (#135778)

Fixes #45326 

When you thread jump by calling 
`j +2` or `thread jump --by +2` the offset is not recognised. This
commit fixes that.

---------

Signed-off-by: Ebuka Ezike <yerimyah1@gmail.com>
diff --git a/lldb/source/Commands/CommandObjectThread.cpp b/lldb/source/Commands/CommandObjectThread.cpp
index 224c523..57c23d5 100644
--- a/lldb/source/Commands/CommandObjectThread.cpp
+++ b/lldb/source/Commands/CommandObjectThread.cpp
@@ -1649,11 +1649,14 @@
           return Status::FromErrorStringWithFormat("invalid line number: '%s'.",
                                                    option_arg.str().c_str());
         break;
-      case 'b':
+      case 'b': {
+        option_arg.consume_front("+");
+
         if (option_arg.getAsInteger(0, m_line_offset))
           return Status::FromErrorStringWithFormat("invalid line offset: '%s'.",
                                                    option_arg.str().c_str());
         break;
+      }
       case 'a':
         m_load_addr = OptionArgParser::ToAddress(execution_context, option_arg,
                                                  LLDB_INVALID_ADDRESS, &error);
diff --git a/lldb/source/Commands/Options.td b/lldb/source/Commands/Options.td
index eb2e511..8aa52c9 100644
--- a/lldb/source/Commands/Options.td
+++ b/lldb/source/Commands/Options.td
@@ -1136,7 +1136,8 @@
   def thread_jump_line : Option<"line", "l">, Group<1>, Arg<"LineNum">,
     Required, Desc<"Specifies the line number to jump to.">;
   def thread_jump_by : Option<"by", "b">, Group<2>, Arg<"Offset">, Required,
-    Desc<"Jumps by a relative line offset from the current line.">;
+    Desc<"Jumps by a relative line offset from the current line,"
+    "can be a positive or negative offset">;
   def thread_jump_address : Option<"address", "a">, Group<3>,
     Arg<"AddressOrExpression">, Required, Desc<"Jumps to a specific address.">;
   def thread_jump_force : Option<"force", "r">, Groups<[1,2,3]>,
diff --git a/lldb/test/API/functionalities/thread/jump/TestThreadJump.py b/lldb/test/API/functionalities/thread/jump/TestThreadJump.py
index 3c13a96..d603580 100644
--- a/lldb/test/API/functionalities/thread/jump/TestThreadJump.py
+++ b/lldb/test/API/functionalities/thread/jump/TestThreadJump.py
@@ -10,9 +10,12 @@
 
 
 class ThreadJumpTestCase(TestBase):
+    def setUp(self):
+        TestBase.setUp(self)
+        self.build()
+
     def test(self):
         """Test thread jump handling."""
-        self.build()
         exe = self.getBuildArtifact("a.out")
         self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
 
@@ -62,6 +65,71 @@
             substrs=["error"],
         )
 
+    def test_jump_offset(self):
+        """Test Thread Jump by negative or positive offset"""
+        exe = self.getBuildArtifact("a.out")
+        file_name = "main.cpp"
+        self.runCmd(f"target create {exe}", CURRENT_EXECUTABLE_SET)
+
+        pos_jump = line_number(file_name, "// jump_offset 1")
+        neg_jump = line_number(file_name, "// jump_offset 2")
+        pos_breakpoint = line_number(file_name, "// breakpoint 1")
+        neg_breakpoint = line_number(file_name, "// breakpoint 2")
+        pos_jump_offset = pos_jump - pos_breakpoint
+        neg_jump_offset = neg_jump - neg_breakpoint
+
+        var_1, var_1_value = ("var_1", "10")
+        var_2, var_2_value = ("var_2", "40")
+        var_3, var_3_value = ("var_3", "10")
+
+        # create pos_breakpoint and neg_breakpoint
+        lldbutil.run_break_set_by_file_and_line(
+            self, file_name, pos_breakpoint, num_expected_locations=1
+        )
+        lldbutil.run_break_set_by_file_and_line(
+            self, file_name, neg_breakpoint, num_expected_locations=1
+        )
+
+        self.runCmd("run", RUN_SUCCEEDED)
+
+        # test positive jump
+        # The stop reason of the thread should be breakpoint 1.
+        self.expect(
+            "thread list",
+            STOPPED_DUE_TO_BREAKPOINT + " 1",
+            substrs=[
+                "stopped",
+                f"{file_name}:{pos_breakpoint}",
+                "stop reason = breakpoint 1",
+            ],
+        )
+
+        self.runCmd(f"thread jump --by +{pos_jump_offset}")
+        self.expect("process status", substrs=[f"at {file_name}:{pos_jump}"])
+        self.expect(f"print {var_1}", substrs=[var_1_value])
+
+        self.runCmd("thread step-over")
+        self.expect(f"print {var_2}", substrs=[var_2_value])
+
+        self.runCmd("continue")
+
+        # test negative jump
+        # The stop reason of the thread should be breakpoint 1.
+        self.expect(
+            "thread list",
+            STOPPED_DUE_TO_BREAKPOINT + " 2",
+            substrs=[
+                "stopped",
+                f"{file_name}:{neg_breakpoint}",
+                "stop reason = breakpoint 2",
+            ],
+        )
+
+        self.runCmd(f"thread jump --by {neg_jump_offset}")
+        self.expect("process status", substrs=[f"at {file_name}:{neg_jump}"])
+        self.runCmd("thread step-over")
+        self.expect(f"print {var_3}", substrs=[var_3_value])
+
     def do_min_test(self, start, jump, var, value):
         # jump to the start marker
         self.runCmd("j %i" % start)
diff --git a/lldb/test/API/functionalities/thread/jump/main.cpp b/lldb/test/API/functionalities/thread/jump/main.cpp
index d3c0de2..9c7f0bc 100644
--- a/lldb/test/API/functionalities/thread/jump/main.cpp
+++ b/lldb/test/API/functionalities/thread/jump/main.cpp
@@ -13,6 +13,21 @@
     }
 }
 
+int jump_positive_offset() {
+  int var_1 = 10;
+  var_1 = 20; // breakpoint 1
+
+  int var_2 = 40; // jump_offset 1
+  return var_2;
+}
+
+int jump_negative_offset() {
+  int var_3 = 10; // jump_offset 2
+  var_3 = 99;
+
+  return var_3; // breakpoint 2
+}
+
 int main ()
 {
     int i;
@@ -22,5 +37,7 @@
     i = min(min_i_a, min_i_b); // 3rd marker
     j = min(min_j_a, min_j_b); // 4th marker
 
+    jump_positive_offset();
+    jump_negative_offset();
     return 0;
 }