[llvm] [z/OS] Add backtrace support for z/OS. (PR #121826)

Kai Nacke via llvm-commits llvm-commits at lists.llvm.org
Wed Jan 8 06:40:24 PST 2025


https://github.com/redstar updated https://github.com/llvm/llvm-project/pull/121826

>From 7406f5bd95d772b5b16599106e9fd318dbc29287 Mon Sep 17 00:00:00 2001
From: Kai Nacke <kai.peter.nacke at ibm.com>
Date: Mon, 6 Jan 2025 14:17:58 -0500
Subject: [PATCH 1/7] [z/OS] Add backtrace support for z/OS.

The system call `__CELQTBCK()` is used to build a backtrace like
on other systems. The collected information are the address of the PC,
the address of the entry point (EP), the difference between both
addresses (+EP), the dynamic storage area (DSA aka the stack
pointer), and the function name.
The system call is described here:
https://www.ibm.com/docs/en/zos/3.1.0?topic=cwicsa6a-celqtbck-also-known-as-celqtbck-64-bit-traceback-service
---
 llvm/lib/Support/Unix/Signals.inc | 81 +++++++++++++++++++++++++++++++
 1 file changed, 81 insertions(+)

diff --git a/llvm/lib/Support/Unix/Signals.inc b/llvm/lib/Support/Unix/Signals.inc
index 088ca33e3c8c56..2a1977b2c94a02 100644
--- a/llvm/lib/Support/Unix/Signals.inc
+++ b/llvm/lib/Support/Unix/Signals.inc
@@ -79,6 +79,10 @@
 #undef HAVE__UNWIND_BACKTRACE
 #endif
 #endif
+#if ENABLE_BACKTRACES && defined(__MVS__)
+#include "llvm/Support/ConvertEBCDIC.h"
+#include <__le_cwi.h>
+#endif
 
 using namespace llvm;
 
@@ -708,6 +712,79 @@ static int unwindBacktrace(void **StackTrace, int MaxEntries) {
 }
 #endif
 
+#if ENABLE_BACKTRACES && defined(__MVS__)
+void zosbacktrace(raw_ostream &OS) {
+  // A function name in the PPA1 can have length 16k.
+  constexpr size_t MAX_ENTRY_NAME = UINT16_MAX;
+  // Limit all other strings to 8 byte.
+  constexpr size_t MAX_OTHER = 8;
+  void *dsaptr;                         // Input
+  int32_t dsa_format = -1;              // Input/Output
+  void *caaptr = _gtca();               // Input
+  int32_t member_id;
+  char compile_unit_name[MAX_OTHER];
+  int32_t compile_unit_name_length = sizeof(compile_unit_name); // Input/Output
+  void *compile_unit_address;                                   // Output
+  void *call_instruction_address = 0;                           // Input/Output
+  char entry_name[MAX_ENTRY_NAME];                              // Output
+  int32_t entry_name_length = sizeof(entry_name);               // Input/Output
+  void *entry_address;                                          // Output
+  void *callers_instruction_address;                            // Output
+  void *callers_dsaptr;                                         // Output
+  int32_t callers_dsa_format;                                   // Output
+  char statement_id[MAX_OTHER];                                 // Output
+  int32_t statement_id_length = sizeof(statement_id);           // Input/Output
+  void *cibptr;                                                 // Output
+  int32_t main_program;                                         // Output
+  _FEEDBACK fc;                                                 // Output
+
+  // The DSA pointer is the value of the stack pointer r4. __builtin_frame_address()
+  // returns a pointer to the stack frame, so the stack bias has to be considered
+  // to get the expected DSA value.
+  dsaptr = static_cast<char *>(__builtin_frame_address(0)) - 2048;
+  int count = 0;
+  OS << " DSA  Adr                EP                 +EP         DSA           "
+        "     Entry\n";
+  while (1) {
+    compile_unit_name_length = sizeof(compile_unit_name);
+    entry_name_length = sizeof(entry_name);
+    statement_id_length = sizeof(statement_id);
+    // See
+    // https://www.ibm.com/docs/en/zos/3.1.0?topic=cwicsa6a-celqtbck-also-known-as-celqtbck-64-bit-traceback-service
+    // for documentation of the parameters.
+    __CELQTBCK(&dsaptr, &dsa_format, &caaptr, &member_id, &compile_unit_name[0],
+               &compile_unit_name_length, &compile_unit_address,
+               &call_instruction_address, &entry_name[0], &entry_name_length,
+               &entry_address, &callers_instruction_address, &callers_dsaptr,
+               &callers_dsa_format, &statement_id[0], &statement_id_length,
+               &cibptr, &main_program, &fc);
+    if (fc.tok_sev) {
+      OS << format("Error! Severity %d Message %d\n", fc.tok_sev, fc.tok_msgno);
+      break;
+    }
+
+    if (count) { // Omit first entry.
+      uintptr_t diff = reinterpret_cast<uintptr_t>(call_instruction_address) -
+                       reinterpret_cast<uintptr_t>(entry_address);
+      OS << format(" %3d. 0x%016X", count, call_instruction_address);
+      OS << format(" 0x%016X +0x%08X 0x%016X", entry_address, diff, dsaptr);
+      SmallString<256> Str;
+      ConverterEBCDIC::convertToUTF8(StringRef(entry_name, entry_name_length),
+                                     Str);
+      OS << format(" %s", Str.c_str());
+      OS << "\n";
+    }
+    count++;
+    if (callers_dsaptr) {
+      dsaptr = callers_dsaptr;
+      dsa_format = callers_dsa_format;
+      call_instruction_address = callers_instruction_address;
+    } else
+      break;
+  }
+}
+#endif
+
 // In the case of a program crash or fault, print out a stack trace so that the
 // user has an indication of why and where we died.
 //
@@ -715,6 +792,9 @@ static int unwindBacktrace(void **StackTrace, int MaxEntries) {
 // doesn't demangle symbols.
 void llvm::sys::PrintStackTrace(raw_ostream &OS, int Depth) {
 #if ENABLE_BACKTRACES
+#ifdef __MVS__
+  zosbacktrace(OS);
+#else
   static void *StackTrace[256];
   int depth = 0;
 #if defined(HAVE_BACKTRACE)
@@ -791,6 +871,7 @@ void llvm::sys::PrintStackTrace(raw_ostream &OS, int Depth) {
   backtrace_symbols_fd(StackTrace, Depth, STDERR_FILENO);
 #endif
 #endif
+#endif
 }
 
 static void PrintStackTraceSignalHandler(void *) {

>From 5d0426310aeb01dd016985142cc45e973a541835 Mon Sep 17 00:00:00 2001
From: Kai Nacke <kai.peter.nacke at ibm.com>
Date: Mon, 6 Jan 2025 15:54:46 -0500
Subject: [PATCH 2/7] clang-format changes

---
 llvm/lib/Support/Unix/Signals.inc | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/llvm/lib/Support/Unix/Signals.inc b/llvm/lib/Support/Unix/Signals.inc
index 2a1977b2c94a02..5237323257f070 100644
--- a/llvm/lib/Support/Unix/Signals.inc
+++ b/llvm/lib/Support/Unix/Signals.inc
@@ -718,9 +718,9 @@ void zosbacktrace(raw_ostream &OS) {
   constexpr size_t MAX_ENTRY_NAME = UINT16_MAX;
   // Limit all other strings to 8 byte.
   constexpr size_t MAX_OTHER = 8;
-  void *dsaptr;                         // Input
-  int32_t dsa_format = -1;              // Input/Output
-  void *caaptr = _gtca();               // Input
+  void *dsaptr;            // Input
+  int32_t dsa_format = -1; // Input/Output
+  void *caaptr = _gtca();  // Input
   int32_t member_id;
   char compile_unit_name[MAX_OTHER];
   int32_t compile_unit_name_length = sizeof(compile_unit_name); // Input/Output
@@ -738,9 +738,9 @@ void zosbacktrace(raw_ostream &OS) {
   int32_t main_program;                                         // Output
   _FEEDBACK fc;                                                 // Output
 
-  // The DSA pointer is the value of the stack pointer r4. __builtin_frame_address()
-  // returns a pointer to the stack frame, so the stack bias has to be considered
-  // to get the expected DSA value.
+  // The DSA pointer is the value of the stack pointer r4.
+  // __builtin_frame_address() returns a pointer to the stack frame, so the
+  // stack bias has to be considered to get the expected DSA value.
   dsaptr = static_cast<char *>(__builtin_frame_address(0)) - 2048;
   int count = 0;
   OS << " DSA  Adr                EP                 +EP         DSA           "

>From 9b9de19cdcd795f67be1d9e88fe6d2debbca5a23 Mon Sep 17 00:00:00 2001
From: Kai Nacke <kai.peter.nacke at ibm.com>
Date: Tue, 7 Jan 2025 10:06:51 -0500
Subject: [PATCH 3/7] Address reviewer comments

---
 llvm/lib/Support/Unix/Signals.inc | 11 ++++++-----
 1 file changed, 6 insertions(+), 5 deletions(-)

diff --git a/llvm/lib/Support/Unix/Signals.inc b/llvm/lib/Support/Unix/Signals.inc
index 5237323257f070..19e454eeb381ea 100644
--- a/llvm/lib/Support/Unix/Signals.inc
+++ b/llvm/lib/Support/Unix/Signals.inc
@@ -725,7 +725,7 @@ void zosbacktrace(raw_ostream &OS) {
   char compile_unit_name[MAX_OTHER];
   int32_t compile_unit_name_length = sizeof(compile_unit_name); // Input/Output
   void *compile_unit_address;                                   // Output
-  void *call_instruction_address = 0;                           // Input/Output
+  void *call_instruction_address = nullptr;                     // Input/Output
   char entry_name[MAX_ENTRY_NAME];                              // Output
   int32_t entry_name_length = sizeof(entry_name);               // Input/Output
   void *entry_address;                                          // Output
@@ -741,7 +741,7 @@ void zosbacktrace(raw_ostream &OS) {
   // The DSA pointer is the value of the stack pointer r4.
   // __builtin_frame_address() returns a pointer to the stack frame, so the
   // stack bias has to be considered to get the expected DSA value.
-  dsaptr = static_cast<char *>(__builtin_frame_address(0)) - 2048;
+  void *dsaptr = static_cast<char *>(__builtin_frame_address(0)) - 2048;
   int count = 0;
   OS << " DSA  Adr                EP                 +EP         DSA           "
         "     Entry\n";
@@ -759,7 +759,8 @@ void zosbacktrace(raw_ostream &OS) {
                &callers_dsa_format, &statement_id[0], &statement_id_length,
                &cibptr, &main_program, &fc);
     if (fc.tok_sev) {
-      OS << format("Error! Severity %d Message %d\n", fc.tok_sev, fc.tok_msgno);
+      OS << format("error: CELQTBCK returned severity %d message %d\n",
+                   fc.tok_sev, fc.tok_msgno);
       break;
     }
 
@@ -772,9 +773,9 @@ void zosbacktrace(raw_ostream &OS) {
       ConverterEBCDIC::convertToUTF8(StringRef(entry_name, entry_name_length),
                                      Str);
       OS << format(" %s", Str.c_str());
-      OS << "\n";
+      OS << '\n';
     }
-    count++;
+    ++count;
     if (callers_dsaptr) {
       dsaptr = callers_dsaptr;
       dsa_format = callers_dsa_format;

>From f37ec9b1d1fb954bd4d1d01457a2b5ef0258a078 Mon Sep 17 00:00:00 2001
From: Kai Nacke <kai.peter.nacke at ibm.com>
Date: Tue, 7 Jan 2025 10:08:29 -0500
Subject: [PATCH 4/7] Do not declare variable twice.

---
 llvm/lib/Support/Unix/Signals.inc | 1 -
 1 file changed, 1 deletion(-)

diff --git a/llvm/lib/Support/Unix/Signals.inc b/llvm/lib/Support/Unix/Signals.inc
index 19e454eeb381ea..e0ec3fcbac80bf 100644
--- a/llvm/lib/Support/Unix/Signals.inc
+++ b/llvm/lib/Support/Unix/Signals.inc
@@ -718,7 +718,6 @@ void zosbacktrace(raw_ostream &OS) {
   constexpr size_t MAX_ENTRY_NAME = UINT16_MAX;
   // Limit all other strings to 8 byte.
   constexpr size_t MAX_OTHER = 8;
-  void *dsaptr;            // Input
   int32_t dsa_format = -1; // Input/Output
   void *caaptr = _gtca();  // Input
   int32_t member_id;

>From 1eb861ffa17418657ad04bdf17724e838ebdee8e Mon Sep 17 00:00:00 2001
From: Kai Nacke <kai.peter.nacke at ibm.com>
Date: Tue, 7 Jan 2025 10:27:21 -0500
Subject: [PATCH 5/7] Move another set of declarations.

---
 llvm/lib/Support/Unix/Signals.inc | 40 +++++++++++++++----------------
 1 file changed, 19 insertions(+), 21 deletions(-)

diff --git a/llvm/lib/Support/Unix/Signals.inc b/llvm/lib/Support/Unix/Signals.inc
index e0ec3fcbac80bf..3a03b3221c0927 100644
--- a/llvm/lib/Support/Unix/Signals.inc
+++ b/llvm/lib/Support/Unix/Signals.inc
@@ -718,24 +718,21 @@ void zosbacktrace(raw_ostream &OS) {
   constexpr size_t MAX_ENTRY_NAME = UINT16_MAX;
   // Limit all other strings to 8 byte.
   constexpr size_t MAX_OTHER = 8;
-  int32_t dsa_format = -1; // Input/Output
-  void *caaptr = _gtca();  // Input
-  int32_t member_id;
-  char compile_unit_name[MAX_OTHER];
-  int32_t compile_unit_name_length = sizeof(compile_unit_name); // Input/Output
-  void *compile_unit_address;                                   // Output
-  void *call_instruction_address = nullptr;                     // Input/Output
-  char entry_name[MAX_ENTRY_NAME];                              // Output
-  int32_t entry_name_length = sizeof(entry_name);               // Input/Output
-  void *entry_address;                                          // Output
-  void *callers_instruction_address;                            // Output
-  void *callers_dsaptr;                                         // Output
-  int32_t callers_dsa_format;                                   // Output
-  char statement_id[MAX_OTHER];                                 // Output
-  int32_t statement_id_length = sizeof(statement_id);           // Input/Output
-  void *cibptr;                                                 // Output
-  int32_t main_program;                                         // Output
-  _FEEDBACK fc;                                                 // Output
+  int32_t dsa_format = -1;                  // Input/Output
+  void *caaptr = _gtca();                   // Input
+  int32_t member_id;                        // Output
+  char compile_unit_name[MAX_OTHER];        // Output
+  void *compile_unit_address;               // Output
+  void *call_instruction_address = nullptr; // Input/Output
+  char entry_name[MAX_ENTRY_NAME];          // Output
+  void *entry_address;                      // Output
+  void *callers_instruction_address;        // Output
+  void *callers_dsaptr;                     // Output
+  int32_t callers_dsa_format;               // Output
+  char statement_id[MAX_OTHER];             // Output
+  void *cibptr;                             // Output
+  int32_t main_program;                     // Output
+  _FEEDBACK fc;                             // Output
 
   // The DSA pointer is the value of the stack pointer r4.
   // __builtin_frame_address() returns a pointer to the stack frame, so the
@@ -745,9 +742,10 @@ void zosbacktrace(raw_ostream &OS) {
   OS << " DSA  Adr                EP                 +EP         DSA           "
         "     Entry\n";
   while (1) {
-    compile_unit_name_length = sizeof(compile_unit_name);
-    entry_name_length = sizeof(entry_name);
-    statement_id_length = sizeof(statement_id);
+    // After the call, these variables contain the length of the string.
+    int32_t compile_unit_name_length = sizeof(compile_unit_name);
+    int32_t entry_name_length = sizeof(entry_name);
+    int32_t statement_id_length = sizeof(statement_id);
     // See
     // https://www.ibm.com/docs/en/zos/3.1.0?topic=cwicsa6a-celqtbck-also-known-as-celqtbck-64-bit-traceback-service
     // for documentation of the parameters.

>From 69bc6c32bf7744c663840bed9debdf77b1bdeca0 Mon Sep 17 00:00:00 2001
From: Kai Nacke <kai at redstar.de>
Date: Wed, 8 Jan 2025 09:36:06 -0500
Subject: [PATCH 6/7] Update llvm/lib/Support/Unix/Signals.inc

Add static to zosbacktrace

Co-authored-by: Matt Arsenault <arsenm2 at gmail.com>
---
 llvm/lib/Support/Unix/Signals.inc | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/lib/Support/Unix/Signals.inc b/llvm/lib/Support/Unix/Signals.inc
index 3a03b3221c0927..d59cecc119913e 100644
--- a/llvm/lib/Support/Unix/Signals.inc
+++ b/llvm/lib/Support/Unix/Signals.inc
@@ -713,7 +713,7 @@ static int unwindBacktrace(void **StackTrace, int MaxEntries) {
 #endif
 
 #if ENABLE_BACKTRACES && defined(__MVS__)
-void zosbacktrace(raw_ostream &OS) {
+static void zosbacktrace(raw_ostream &OS) {
   // A function name in the PPA1 can have length 16k.
   constexpr size_t MAX_ENTRY_NAME = UINT16_MAX;
   // Limit all other strings to 8 byte.

>From df700ff2c1c825e6ca1f8b8011d233f6ed5b1309 Mon Sep 17 00:00:00 2001
From: Kai Nacke <kai.peter.nacke at ibm.com>
Date: Wed, 8 Jan 2025 09:40:04 -0500
Subject: [PATCH 7/7] Remove call to format()

---
 llvm/lib/Support/Unix/Signals.inc | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/llvm/lib/Support/Unix/Signals.inc b/llvm/lib/Support/Unix/Signals.inc
index d59cecc119913e..e20f212b9612c8 100644
--- a/llvm/lib/Support/Unix/Signals.inc
+++ b/llvm/lib/Support/Unix/Signals.inc
@@ -769,8 +769,7 @@ static void zosbacktrace(raw_ostream &OS) {
       SmallString<256> Str;
       ConverterEBCDIC::convertToUTF8(StringRef(entry_name, entry_name_length),
                                      Str);
-      OS << format(" %s", Str.c_str());
-      OS << '\n';
+      OS << ' ' << Str << '\n';
     }
     ++count;
     if (callers_dsaptr) {



More information about the llvm-commits mailing list