diff mbox series

[v2,8/8] cpp-example: prevent compiler from eliminating debugger test code

Message ID 20260809093629.3457107-9-adrian.freihofer@siemens.com
State New
Headers show
Series oe-selftest: devtool ide-sdk: fix lldb connect race in _lldb_server_debugging_once | expand

Commit Message

AdrianF Aug. 9, 2026, 9:35 a.m. UTC
From: Adrian Freihofer <adrian.freihofer@siemens.com>

Be more paranoid about compiler optimizations that can eliminate code
that is only used for debugger inspection.

scale_number() in cpp-example-lib.hpp: use 'volatile int scaled' to
prevent the compiler from inlining the function body away entirely,
which left no out-of-line address for a breakpoint.

cpp-example.cpp: initialize the std::vector from volatile variables so
the compiler cannot constant-fold the values and eliminate the for-loop
body.  Pass numbers[0]+numbers[1]+numbers[2] (= 6) as the scale_number
argument to keep the vector live at the call site (exe_break_line=63);
the test assertion '$4 = 6' is unchanged since 1+2+3 == 6.

Update the test accordingly: exe_break_line moves from 56 to 63, the
LINE_SHIFT anchor changes to the volatile declaration line, and the
list-output assertion matches '{n1, n2, n3}'.

Note: the test was originally written to also check that the compiler
uses e.g. -O0 to avoid eliminating e.g. the vector, but that should
probably be tested separately. Changing this ide-sdk test to use
volatile variables is a more robust way to ensure the vector is not
eliminated, regardless of compiler flags.

Signed-off-by: Adrian Freihofer <adrian.freihofer@siemens.com>
---
 .../recipes-test/cpp/files/cpp-example-lib.hpp  |  6 ++++--
 .../recipes-test/cpp/files/cpp-example.cpp      | 10 +++++-----
 meta/lib/oeqa/selftest/cases/devtool.py         | 17 +++++++++--------
 3 files changed, 18 insertions(+), 15 deletions(-)
diff mbox series

Patch

diff --git a/meta-selftest/recipes-test/cpp/files/cpp-example-lib.hpp b/meta-selftest/recipes-test/cpp/files/cpp-example-lib.hpp
index d1c9bca416..5af30e2a79 100644
--- a/meta-selftest/recipes-test/cpp/files/cpp-example-lib.hpp
+++ b/meta-selftest/recipes-test/cpp/files/cpp-example-lib.hpp
@@ -15,10 +15,12 @@  struct CppExample
     inline static const std::string test_string = "cpp-example-lib Magic: 123456789";
 
     /* Header-only function, to exercise breakpoint resolution against
-     * header-only debug info. */
+     * header-only debug info. volatile prevents compiler optimization from
+     * eliminating the function body, ensuring a concrete code location exists
+     * for debugger breakpoints. */
     inline static int scale_number(int n)
     {
-        int scaled = n * 7;
+        volatile int scaled = n * 7;
         std::cout << "scale_number(" << n << ") = " << scaled << std::endl;
         return scaled;
     }
diff --git a/meta-selftest/recipes-test/cpp/files/cpp-example.cpp b/meta-selftest/recipes-test/cpp/files/cpp-example.cpp
index ad1abae257..a376419c13 100644
--- a/meta-selftest/recipes-test/cpp/files/cpp-example.cpp
+++ b/meta-selftest/recipes-test/cpp/files/cpp-example.cpp
@@ -50,17 +50,17 @@  int main(int argc, char* argv[])
             sleep(1);
         }
     } while (endless_mode);
-
+    volatile int n1 = 1, n2 = 2, n3 = 3;
     // Example: Demonstrate std::vector traversal for debugger inspection
-    std::vector<int> numbers = {1, 2, 3};
+    std::vector<int> numbers = {n1, n2, n3};
     std::cout << "Traversing std::vector<int> numbers:" << std::endl;
     for (size_t i = 0; i < numbers.size(); ++i) {
         std::cout << "numbers[" << i << "] = " << numbers[i] << std::endl;
     }
 
-    // Example: call a header-only function once, to exercise breakpoint
-    // resolution against header-only debug info.
-    CppExample::scale_number(6);
+    // Pass numbers elements as the argument so the compiler cannot eliminate
+    // the vector; 1+2+3 == 6, so the scale_number(n) check is unchanged.
+    CppExample::scale_number(numbers[0] + numbers[1] + numbers[2]);
 
     return 0;
 }
diff --git a/meta/lib/oeqa/selftest/cases/devtool.py b/meta/lib/oeqa/selftest/cases/devtool.py
index 06be7ef2df..7338b85d7f 100644
--- a/meta/lib/oeqa/selftest/cases/devtool.py
+++ b/meta/lib/oeqa/selftest/cases/devtool.py
@@ -2975,8 +2975,8 @@  class DevtoolIdeSdkTests(DevtoolBase):
         with open(cpp_example_cpp, 'r') as file:
             cpp_code = file.read()
             cpp_code = cpp_code.replace(
-                "    std::vector<int> numbers = {1, 2, 3};",
-                extra_lines + "    std::vector<int> numbers = {1, 2, 3};")
+                "    volatile int n1 = 1, n2 = 2, n3 = 3;",
+                extra_lines + "    volatile int n1 = 1, n2 = 2, n3 = 3;")
         with open(cpp_example_cpp, 'w') as file:
             file.write(cpp_code)
 
@@ -3016,7 +3016,7 @@  class DevtoolIdeSdkTests(DevtoolBase):
         # the first _gdb_cross_debugging_multi call above.
         self._gdb_cross_debugging_multi(
             qemu, recipe_name, example_exe, MAGIC_STRING_NEW,
-            exe_break_line=56 + LINE_SHIFT, exe_list_line=55 + LINE_SHIFT,
+            exe_break_line=63 + LINE_SHIFT, exe_list_line=55 + LINE_SHIFT,
             hpp_break_line=21 + LINE_SHIFT, lib_break_line=31 + LINE_SHIFT)
 
     def _gdb_cross(self):
@@ -3033,7 +3033,7 @@  class DevtoolIdeSdkTests(DevtoolBase):
         self.assertIn("GNU gdb", r.output)
 
     def _gdb_debug_cpp_example(self, magic_string, gdb_start_cmd="run",
-                              exe_break_line=56, exe_list_line=55, hpp_break_line=21,
+                              exe_break_line=63, exe_list_line=55, hpp_break_line=21,
                               lib_break_line=31):
         """Get a series of gdb commands to debug the cpp-example-lib example"""
         gdb_batch_cmd = " -ex 'break main' -ex '%s'" % gdb_start_cmd
@@ -3057,8 +3057,9 @@  class DevtoolIdeSdkTests(DevtoolBase):
 
         # check if resolving std::vector works with python scripts
         gdb_batch_cmd += " -ex 'list cpp-example.cpp:%d,%d'" % (exe_list_line, exe_list_line)
-        # Break on exe_break_line (the std::cout after the declaration) so the
-        # vector constructor on exe_list_line has already run when GDB stops.
+        # Break on exe_break_line (the scale_number call) so the vector on
+        # exe_list_line is both constructed and referenced; the compiler cannot
+        # eliminate the vector because its elements are passed as the argument.
         # These line numbers shift after the test inserts extra lines and
         # recompiles, proving the breakpoint resolves via the freshly rebuilt
         # debug info rather than a stale, cached line-to-address mapping.
@@ -3095,7 +3096,7 @@  class DevtoolIdeSdkTests(DevtoolBase):
 
         # check if resolving std::vector works with python scripts
         self.assertRegex(
-            gdb_output, r"%d\s+std::vector<int> numbers = \{1, 2, 3\};" % exe_list_line)
+            gdb_output, r"%d\s+std::vector<int> numbers = \{n1, n2, n3\};" % exe_list_line)
         self.assertIn("$3 = std::vector of length 3, capacity 3 = {1, 2, 3}", gdb_output)
 
         # check that a breakpoint in an inline function defined directly in
@@ -3106,7 +3107,7 @@  class DevtoolIdeSdkTests(DevtoolBase):
         self.assertIn("exited normally", gdb_output)
 
     def _gdb_cross_debugging_multi(self, qemu, recipe_name, example_exe, magic_string,
-                                   exe_break_line=56, exe_list_line=55, hpp_break_line=21,
+                                   exe_break_line=63, exe_list_line=55, hpp_break_line=21,
                                    lib_break_line=31):
         """Verify gdb-cross is working