With kernel commit 0990d836ce "s390/debug: debug feature version 3" the
__debug_entry structure has been modified by:
- removing redundant union
- expanding microseconds timestamp to 60 bits and storing it in the
absolute Unix time format taking Epoch Index into account
- expanding cpuid field to 16 bits
Current crash patch aims to:
- define debug_entry_v3_t data type for new debug entry structure
and process V3 debug entries accordingly
- adjust debug entries header output by setting minimum width for
cpuid to 4 digits and expanding function name width to 26 characters
- adjust output alignment for the header and debug entries
- minor code cleanup
Signed-off-by: Mikhail Zaslonko <zaslonko(a)linux.ibm.com>
---
s390dbf.c | 130 +++++++++++++++++++++++++++++++-----------------------
1 file changed, 74 insertions(+), 56 deletions(-)
diff --git a/s390dbf.c b/s390dbf.c
index 232ceb8..5f53170 100644
--- a/s390dbf.c
+++ b/s390dbf.c
@@ -165,6 +165,7 @@ static inline int set_cmd_flags(command_t *cmd, int flags, char
*extraops)
return 0;
}
+#define USEC_PER_SEC 1000000L
/* Time of day clock value for 1970/01/01 */
#define TOD_UNIX_EPOCH (0x8126d60e46000000LL - (0x3c26700LL * 1000000 * 4096))
/* Time of day clock value for 1970/01/01 in usecs */
@@ -180,8 +181,8 @@ static inline void kl_s390tod_to_timeval(uint64_t todval, struct
timeval *xtime)
todval_us += tod_clock_base_us;
/* Subtract EPOCH that we get time in usec since 1970 */
todval_us -= TOD_UNIX_EPOCH_US;
- xtime->tv_sec = todval_us / 1000000;
- xtime->tv_usec = todval_us % 1000000;
+ xtime->tv_sec = todval_us / USEC_PER_SEC;
+ xtime->tv_usec = todval_us % USEC_PER_SEC;
}
static inline int kl_struct_len(char* struct_name)
@@ -227,6 +228,7 @@ static inline kaddr_t kl_funcaddr(kaddr_t addr)
#define DBF_VERSION_V1 1
#define DBF_VERSION_V2 2
+#define DBF_VERSION_V3 3
#define PAGE_SIZE 4096
#define DEBUG_MAX_VIEWS 10 /* max number of views in proc fs */
#define DEBUG_MAX_PROCF_LEN 64 /* max length for a proc file name */
@@ -238,8 +240,9 @@ static inline kaddr_t kl_funcaddr(kaddr_t addr)
typedef struct debug_view_s debug_view_t;
/* struct to hold contents of struct __debug_entry from dump
+ * for DBF_VERSION_V1 and DBF_VERSION_V2
*/
-typedef struct debug_entry_s{
+typedef struct debug_entry_v1_s{
union {
struct {
unsigned long long clock:52;
@@ -251,9 +254,16 @@ typedef struct debug_entry_s{
unsigned long long stck;
} id;
kaddr_t caller; /* changed from void* to kaddr_t */
-} __attribute__((packed)) debug_entry_t;
-/* typedef struct __debug_entry debug_entry_t; */
+} __attribute__((packed)) debug_entry_v1_t;
+/* for DBF_VERSION_V3 */
+typedef struct debug_entry_v3_s{
+ unsigned long long clock:60;
+ unsigned long long exception:1;
+ unsigned long long level:3;
+ kaddr_t caller; /* changed from void* to kaddr_t */
+ unsigned short cpuid;
+} __attribute__((packed)) debug_entry_v3_t;
static unsigned int dbf_version;
@@ -284,7 +294,7 @@ typedef struct debug_info_s {
/* functions to generate dbf output
*/
typedef int (debug_header_proc_t) (debug_info_t* id, debug_view_t* view,
- int area, debug_entry_t* entry,
+ int area, void* entry,
char* out_buf);
typedef int (debug_format_proc_t) (debug_info_t* id, debug_view_t* view,
char* out_buf, const char* in_buf);
@@ -322,35 +332,45 @@ EBCASC(char *inout, size_t len)
*/
static int
dflt_header_fn(debug_info_t * id, debug_view_t *view,
- int area, debug_entry_t * entry, char *out_buf)
+ int area, void *entry, char *out_buf)
{
struct timeval time_val;
- unsigned long long time;
- char *except_str;
- kaddr_t caller;
int rc = 0;
+ unsigned long long time;
+ unsigned short level = 0, cpuid = 0;
+ char *except_str = "-";
+ kaddr_t caller = 0;
char *caller_name;
+ int name_width = 26;
int offset;
char caller_buf[30];
- unsigned int level;
syment_t *caller_sym;
- debug_entry_t lentry; /* store byte swapped values of entry */
- lentry.id.stck = KL_GET_UINT64(&entry->id);
- lentry.caller = KL_GET_PTR(&entry->caller);
- level = lentry.id.fields.level;
- time = lentry.id.stck;
-
- kl_s390tod_to_timeval(time, &time_val);
+ switch (dbf_version) {
+ case DBF_VERSION_V1:
+ case DBF_VERSION_V2:
+ level = ((debug_entry_v1_t *) entry)->id.fields.level;
+ cpuid = ((debug_entry_v1_t *) entry)->id.fields.cpuid;
+ time = ((debug_entry_v1_t *) entry)->id.stck;
+ if (((debug_entry_v1_t *) entry)->id.fields.exception)
+ except_str = "*";
+ caller = ((debug_entry_v1_t *) entry)->caller;
+ kl_s390tod_to_timeval(time, &time_val);
+ break;
+ case DBF_VERSION_V3:
+ level = ((debug_entry_v3_t *) entry)->level;
+ cpuid = ((debug_entry_v3_t *) entry)->cpuid;
+ time = ((debug_entry_v3_t *) entry)->clock;
+ if (((debug_entry_v3_t *) entry)->exception)
+ except_str = "*";
+ caller = ((debug_entry_v3_t *) entry)->caller;
+ time_val.tv_sec = time / USEC_PER_SEC;
+ time_val.tv_usec = time % USEC_PER_SEC;
+ break;
+ }
- if (lentry.id.fields.exception)
- except_str = "*";
- else
- except_str = "-";
- caller = lentry.caller;
- if(KL_ARCH == KL_ARCH_S390){
+ if(KL_ARCH == KL_ARCH_S390)
caller &= 0x7fffffff;
- }
caller_sym = kl_lkup_symaddr(caller);
if(caller_sym){
caller_name = caller_sym->s_name;
@@ -361,20 +381,12 @@ dflt_header_fn(debug_info_t * id, debug_view_t *view,
caller_name = caller_buf;
offset = 0;
}
+ rc += sprintf(out_buf,
+ "%02i %011lu:%06lu %1u %1s %04i <%-*s+%04i> ",
+ area, time_val.tv_sec, time_val.tv_usec, level,
+ except_str, cpuid,
+ name_width, caller_name, offset);
- if(KL_ARCH == KL_ARCH_S390X){
- rc += sprintf(out_buf,
- "%02i %011lu:%06lu %1u %1s %02i <%20s+%04i> ",
- area, time_val.tv_sec, time_val.tv_usec, level,
- except_str, entry->id.fields.cpuid, caller_name,
- offset);
- } else {
- rc += sprintf(out_buf,
- "%02i %011lu:%06lu %1u %1s %02i <%-20s+%04i> ",
- area, time_val.tv_sec, time_val.tv_usec, level,
- except_str, lentry.id.fields.cpuid, caller_name,
- offset);
- }
return rc;
}
@@ -539,9 +551,9 @@ prolog_fn(debug_info_t * id,
{
int rc = 0;
- rc = sprintf(out_buf, "AREA TIME LEVEL EXCEPTION CP CALLING FUNCTION"
- " + OFFSET DATA\n==================================="
- "=======================================\n");
+ rc = sprintf(out_buf, "AREA TIME LEVEL EXCEPTION CP CALLING FUNCTION"
+ " + OFFSET DATA\n==============================="
+ "===========================================\n");
return rc;
}
@@ -663,10 +675,10 @@ debug_view_t sprintf_view = {
};
-static debug_entry_t *
-debug_find_oldest_entry(debug_entry_t *entries, int num, int entry_size)
+static debug_entry_v1_t *
+debug_find_oldest_entry(debug_entry_v1_t *entries, int num, int entry_size)
{
- debug_entry_t *result, *current;
+ debug_entry_v1_t *result, *current;
int i;
uint64_t clock1, clock2;
@@ -681,7 +693,7 @@ debug_find_oldest_entry(debug_entry_t *entries, int num, int
entry_size)
clock2 = KL_GET_UINT64(&clock2);
if (clock1 < clock2)
result = current;
- current = (debug_entry_t *) ((char *) current + entry_size);
+ current = (debug_entry_v1_t *) ((char *) current + entry_size);
}
return result;
}
@@ -697,7 +709,7 @@ debug_format_output_v1(debug_info_t * debug_area, debug_view_t *view,
{
int i, j, len;
int nr_of_entries;
- debug_entry_t *act_entry, *last_entry;
+ debug_entry_v1_t *act_entry, *last_entry;
char *act_entry_data;
char buf[2048];
size_t items ATTRIBUTE_UNUSED;
@@ -720,7 +732,7 @@ debug_format_output_v1(debug_info_t * debug_area, debug_view_t *view,
act_entry = debug_find_oldest_entry(debug_area->areas[i],
nr_of_entries,
debug_area->entry_size);
- last_entry = (debug_entry_t *) ((char *) debug_area->areas[i] +
+ last_entry = (debug_entry_v1_t *) ((char *) debug_area->areas[i] +
(PAGE_SIZE << debug_area->page_order) -
debug_area->entry_size);
for (j = 0; j < nr_of_entries; j++) {
@@ -740,7 +752,7 @@ debug_format_output_v1(debug_info_t * debug_area, debug_view_t *view,
memset(buf, 0, 2048);
}
act_entry =
- (debug_entry_t *) (((char *) act_entry) +
+ (debug_entry_v1_t *) (((char *) act_entry) +
debug_area->entry_size);
if (act_entry > last_entry)
act_entry = debug_area->areas[i];
@@ -759,7 +771,7 @@ debug_format_output_v2(debug_info_t * debug_area,
debug_view_t *view, FILE * ofp)
{
int i, j, k, len;
- debug_entry_t *act_entry;
+ void *act_entry;
char *act_entry_data;
char buf[2048];
size_t items ATTRIBUTE_UNUSED;
@@ -783,7 +795,11 @@ debug_format_output_v2(debug_info_t * debug_area,
act_entry = debug_area->areas_v2[i][j];
for (k = 0; k < nr_entries_per_page; k++) {
act_entry_data = (char*)act_entry + dbe_size;
- if (act_entry->id.stck == 0)
+ if (dbf_version == DBF_VERSION_V3 &&
+ ((debug_entry_v3_t *) act_entry)->clock == 0)
+ break; /* empty entry */
+ else if (dbf_version < DBF_VERSION_V3 &&
+ ((debug_entry_v1_t *) act_entry)->id.stck == 0)
break; /* empty entry */
if (view->header_proc) {
len = view->header_proc(debug_area,
@@ -797,8 +813,8 @@ debug_format_output_v2(debug_info_t * debug_area,
items = fwrite(buf,len, 1, ofp);
memset(buf, 0, 2048);
}
- act_entry = (debug_entry_t *) (((char *)
- act_entry) + debug_area->entry_size);
+ act_entry = ((char *) act_entry) +
+ debug_area->entry_size;
}
}
}
@@ -905,7 +921,7 @@ debug_get_areas_v1(debug_info_t* db_info, void* k_dbi)
mem_pos = KL_ULONG(k_dbi,"debug_info","areas");
for (i = 0; i < db_info->nr_areas; i++) {
dbe_addr = KL_VREAD_PTR(mem_pos);
- db_info->areas[i] = (debug_entry_t *) malloc(area_size);
+ db_info->areas[i] = (debug_entry_v1_t *) malloc(area_size);
/* read raw data for debug area */
GET_BLOCK(dbe_addr, area_size, db_info->areas[i]);
mem_pos += KL_NBPW;
@@ -1328,11 +1344,13 @@ s390dbf_cmd(command_t * cmd)
dbf_version = KL_VREAD_UINT32(dbf_version_sym->s_addr);
- if ((dbf_version != DBF_VERSION_V1) && (dbf_version != DBF_VERSION_V2)){
+ if ((dbf_version != DBF_VERSION_V1) &&
+ (dbf_version != DBF_VERSION_V2) &&
+ (dbf_version != DBF_VERSION_V3)) {
fprintf(cmd->efp,"lcrash does not support the"
" debug feature version of the dump kernel:\n");
- fprintf(cmd->efp,"DUMP: %i SUPPORTED: %i and %i\n",
- dbf_version, DBF_VERSION_V1, DBF_VERSION_V2);
+ fprintf(cmd->efp,"DUMP: %i SUPPORTED: %i, %i and %i\n",
+ dbf_version, DBF_VERSION_V1, DBF_VERSION_V2, DBF_VERSION_V3);
return -1;
}
--
2.17.1