Pages

Monday, October 16, 2006

Solaris/SPARC: dbx style regs (registers) output

Recently one of our partners requested for some help in capturing the values of all the registers at the time of a process crash. Their idea is to get all the information that is necessary for a crash dump analysis, programmatically.

It is no surprise to hear such an idea, as majority of the customers may not be interested in installing and running a long list of commands under a debugger whenever there is a process crash. If the application can gather as much information as it can about the state of the process (context of the process in OS terminology) before it goes down, customers can simply send that information back to the software vendor along with their bug report(s).

Since it is a native application, I gave them the following C code that tries to print the values of all global (g0 - g7) and output (o0 - o7) registers of SPARC, just the way dbx does with regs command. Also it prints the values of program counter (PC) and next program counter (nPC). Note that this program is not complete - it does not print the values of local (l0 - l7), input (i0 - i7) registers. Also the values of multiply/divide register (y), processor state register (psr) and the single-precision & double-precision floating-point registers (f0 - f31) are missing. I will try to enhance this code later to include all the missing pieces. However this sample program gave them some idea on how to proceed with their actual plan.

Programming example:

The following example demonstrates the process crash with SIGSEGV in 32- and 64-bit code. Correctness of this code can be verified by comparing the output of the C program with the output of regs command in dbx environment.

% cat regs.c
#include <sys/types.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <limits.h>
#include <ucontext.h>

void signal_handler (int signo, siginfo_t *si, void *data)
{
ucontext_t *uc;
u_int pc;
u_int sp;
gregset_t *regs;
u_int tspage;
struct frame *fp;
int i;

uc = (ucontext_t *) data;

if (signo = SIGSEGV)
{
fprintf(stdout, "Caught signal SEGV\n");

fprintf(stderr, "\n*** SIGNAL TRAPPED: SIGNAL %d ***\n", signo);
fprintf(stderr, "si_signo = %d\n", si->si_signo);
fprintf(stderr, "si_code = %d\n", si->si_code);
fprintf(stderr, "si_errno = %d\n", si->si_errno);
fprintf(stderr, "si_addr = %p\n", si->si_addr);
fprintf(stderr, "si_trapno= %p\n", si->si_trapno);
fprintf(stderr, "si_pc = %p\n\n", si->si_pc);

fprintf(stderr, "stack info:\nsp: 0x%#016p size: %#016x flags: %d\n\n",
uc->uc_stack.ss_sp, uc->uc_stack.ss_size, uc->uc_stack.ss_flags);

fprintf(stderr, "\ng0-g1 0x0000000000000000 0x%#016p", uc->uc_mcontext.gregs[REG_G1]);
fprintf(stderr, "\ng2-g3 0x%#016p 0x%#016p", uc->uc_mcontext.gregs[REG_G2], uc->uc_mcontext.gregs[REG_G3]);
fprintf(stderr, "\ng4-g5 0x%#016p 0x%#016p", uc->uc_mcontext.gregs[REG_G4], uc->uc_mcontext.gregs[REG_G5]);
fprintf(stderr, "\ng6-g7 0x%#016p 0x%#016p", uc->uc_mcontext.gregs[REG_G6], uc->uc_mcontext.gregs[REG_G7]);
fprintf(stderr, "\no0-o1 0x%#016p 0x%#016p", uc->uc_mcontext.gregs[REG_O0], uc->uc_mcontext.gregs[REG_O1]);
fprintf(stderr, "\no2-o3 0x%#016p 0x%#016p", uc->uc_mcontext.gregs[REG_O2], uc->uc_mcontext.gregs[REG_O3]);
fprintf(stderr, "\no4-o5 0x%#016p 0x%#016p", uc->uc_mcontext.gregs[REG_O4], uc->uc_mcontext.gregs[REG_O5]);
fprintf(stderr, "\no6-o7 0x%#016p 0x%#016p", uc->uc_mcontext.gregs[REG_O6], uc->uc_mcontext.gregs[REG_O7]);
fprintf(stderr, "\npc 0x%#016p", uc->uc_mcontext.gregs[REG_PC]);
fprintf(stderr, "\nnpc 0x%#016p", uc->uc_mcontext.gregs[REG_nPC]);
printf("\n");

exit(1);
}
else
{
fprintf(stdout, "default handler\n");
}
}

int main (void)
{
char *a;
struct sigaction sa, osa;
unsigned int b = ULONG_MAX;

sa.sa_flags = SA_ONSTACK | SA_RESTART | SA_SIGINFO;
sa.sa_sigaction = signal_handler;
sigaction(SIGSEGV, &sa, &osa);

strcat(a, "dummy");
printf("\a = %s", *a);

return b;
}


32-bit code:
% /opt/SUNWspro/prod/bin/cc -o regs regs.c

%./regs
Caught signal SEGV

*** SIGNAL TRAPPED: SIGNAL 11 ***
si_signo = 11
si_code = 1
si_errno = 0
si_addr = ffffffff
si_trapno= 0
si_pc = 0

stack info:
sp: 0x00000000ff400000 size: 0x00000000800000 flags: 0

g0-g1 0x0000000000000000 0x00000000ff2b0ba0
g2-g3 0x0000000000000000 0x0000000000000000
g4-g5 0x0000000000000000 0x0000000000000000
g6-g7 0x0000000000000000 0x00000000ff3a2000
o0-o1 0x00000000ffffffff 0x00000000fffffaf0
o2-o3 0x00000000ffffffff 0x00000000ff3704f8
o4-o5 0x0000000000000003 0x00000000ff3704fc
o6-o7 0x00000000ffbfec88 0x00000000ff316130
pc 0x00000000ff2b0bb8
npc 0x00000000ff2b0bbc

Segmentation Fault (core dumped)

% /opt/SUNWspro/prod/bin/dbx regs core
For information about new features see `help changes'
To remove this message, put `dbxenv suppress_startup_message 7.5' in your .dbxrc
Reading regs
core file header read successfully
Reading ld.so.1
Reading libc.so.1
program terminated by signal SEGV (no mapping at the fault address)
0xffffffffffffffff:

(dbx) up
0xff2c075c: _exithandle+0x003c: call %l7

(dbx) up
0xff2af080: exit+0x0004: call _PROCEDURE_LINKAGE_TABLE_+0xd8 [PLT] ! 0xff36935c

(dbx) up
0x00010e1c: signal_handler+0x0194: call exit [PLT] ! 0x21114

(dbx) up
0xff33fec8: __sighndlr+0x000c: call %i3

(dbx) up
0xff2b0bb8: strlen+0x0018: ldub [%o2], %o1

(dbx) where
[1] 0x6d6d7900(0xffbfe728, 0x1084, 0x0, 0x0, 0x4, 0x0), at 0x6d6d7900
[2] _exithandle(0xff36db80, 0xff3a6475, 0xff36cbc0, 0x0, 0xff3a2000, 0xffbfe950), at 0xff2c075c
[3] exit(0x1, 0x21258, 0x0, 0x21276, 0xff368284, 0x1), at 0xff2af080
[4] signal_handler(0xb, 0xffbfec08, 0xffbfe950, 0x0, 0xff3a2000, 0xffbfe950), at 0x10e1c
[5] __sighndlr(0xb, 0xffbfec08, 0xffbfe950, 0x10c88, 0x0, 0x1), at 0xff33fec8
---- called from signal handler with signal 11 (SIGSEGV) ------
=>[6] strlen(0xffffffff, 0xfffffaf0, 0xffffffff, 0xff3704f8, 0x3, 0xff3704fc), at 0xff2b0bb8
[7] _ndoprnt(0x110d2, 0xffbff9c4, 0xffbff241, 0xffffffff, 0x0, 0x0), at 0xff316130
[8] printf(0x110cc, 0x21258, 0x0, 0x21276, 0xff368284, 0x0), at 0xff3182dc
[9] main(0x1, 0xffbffa8c, 0xffbffa94, 0x21000, 0xff3a00c0, 0xff3a0100), at 0x10e90

(dbx) regs
current frame: [6]
g0-g1 0x00000000 0x00000000 0x00000000 0xff2b0ba0
g2-g3 0x00000000 0x00000000 0x00000000 0x00000000
g4-g5 0x00000000 0x00000000 0x00000000 0x00000000
g6-g7 0x00000000 0x00000000 0x00000000 0xff3a2000
o0-o1 0x00000000 0xffffffff 0x00000000 0xfffffaf0
o2-o3 0x00000000 0xffffffff 0x00000000 0xff3704f8
o4-o5 0x00000000 0x00000003 0x00000000 0xff3704fc
o6-o7 0x00000000 0xffbfec88 0x00000000 0xff316130

l0-l1 0x00000000 0x00000073 0x00000000 0x00000000
l2-l3 0x00000000 0x00000000 0x00000000 0x00001000
l4-l5 0x00000000 0x00000000 0x00000000 0x00000000
l6-l7 0x00000000 0xff36bf69 0x00000000 0xff3708f8
i0-i1 0x00000000 0x000110d2 0x00000000 0xffbff9c4
i2-i3 0x00000000 0xffbff241 0x00000000 0xffffffff
i4-i5 0x00000000 0x00000000 0x00000000 0x00000000
i6-i7 0x00000000 0xffbff918 0x00000000 0xff3182dc
y 0x00000000 0x00000000
ccr 0x00000000 0x00000000
pc 0x00000000 0xff2b0bb8
:strlen+0x18 ldub [%o2], %o1
npc 0x00000000 0xff2b0bbc
:strlen+0x1c tst %o1


64-bit code:
% /opt/SUNWspro/prod/bin/cc -o regs64 -xarch=v9 regs.c

%./regs64
Caught signal SEGV

*** SIGNAL TRAPPED: SIGNAL 11 ***
si_signo = 11
si_code = 1
si_errno = 0
si_addr = ffffffffffffffff
si_trapno= 0
si_pc = 0

stack info:
sp: 0xffffffff7f800000 size: 0x00000000800000 flags: 0

g0-g1 0x0000000000000000 0xffffffff7f239940
g2-g3 0x0000000000000000 0x0000000000000000
g4-g5 0xfffffffffffffad8 0x0000000000000202
g6-g7 0x0000000000000000 0xffffffff7f402000
o0-o1 0xffffffffffffffff 0x0000000000000053
o2-o3 0xffffffffffffffff 0x0000000000000000
o4-o5 0x0000000000000003 0x0000000000000053
o6-o7 0xffffffff7fffe171 0xffffffff7f2a2b54
pc 0xffffffff7f239958
npc 0xffffffff7f23995c

Segmentation Fault (core dumped)

% /opt/SUNWspro/prod/bin/dbx regs64 core
For information about new features see `help changes'
To remove this message, put `dbxenv suppress_startup_message 7.5' in your .dbxrc
Reading regs64
core file header read successfully
Reading ld.so.1
Reading libc.so.1
program terminated by signal SEGV (no mapping at the fault address)
0xffffffff7f249240: _exithandle+0x0044: call %l6

(dbx) up
0xffffffff7f237d10: exit+0x0004: call _PROCEDURE_LINKAGE_TABLE_+0x200 [PLT] ! 0xffffffff7f3e6700

(dbx) up
0x0000000100000b3c: signal_handler+0x01ac: call exit [PLT] ! 0x100100fa0

(dbx) up
0xffffffff7f2cd0bc: __sighndlr+0x000c: call %i3

(dbx) up
0xffffffff7f239958: strlen+0x0018: ldub [%o2], %o1

(dbx) where
[1] _exithandle(0xffffffff7f3eff00, 0xffffffff7fffe170, 0xffffffff7f3eef40, 0xd, 0x0, 0xffffffff7fffe590), at 0xffffffff7f249240

[2] exit(0x1, 0x2000, 0x0, 0x1001012e4, 0xffffffff7f3e4000, 0x1), at 0xffffffff7f237d10
[3] signal_handler(0xb, 0xffffffff7fffe870, 0xffffffff7fffe590, 0xd, 0x0, 0xffffffff7fffe590), at 0x100000b3c
[4] __sighndlr(0xb, 0xffffffff7fffe870, 0xffffffff7fffe590, 0x100000990, 0x0, 0xa), at 0xffffffff7f2cd0bc
---- called from signal handler with signal 11 (SIGSEGV) ------
=>[5] strlen(0xffffffffffffffff, 0x53, 0xffffffffffffffff, 0x0, 0x3, 0x53), at 0xffffffff7f239958
[6] _ndoprnt(0x100000e46, 0xffffffff7ffff878, 0xffffffff7f2a0de4, 0xffffffff7fffefc9, 0xffffffffffffffff, 0x100000e45), at 0xffffffff7f2a2b54

[7] printf(0x100000e40, 0x2000, 0x0, 0x1001012e4, 0xffffffff7f3e4000, 0x0), at 0xffffffff7f2a4de0
[8] main(0x1, 0xffffffff7ffff9b8, 0xffffffff7ffff9c8, 0x0, 0xffffffffffffffff, 0xffffffff7f400100), at 0x100000bc4

(dbx) regs
current frame: [5]
g0-g1 0x0000000000000000 0xffffffff7f239940
g2-g3 0x0000000000000000 0x0000000000000000
g4-g5 0xfffffffffffffad8 0x0000000000000202
g6-g7 0x0000000000000000 0xffffffff7f402000
o0-o1 0xffffffffffffffff 0x0000000000000053
o2-o3 0xffffffffffffffff 0x0000000000000000
o4-o5 0x0000000000000003 0x0000000000000053
o6-o7 0xffffffff7fffe171 0xffffffff7f2a2b54

l0-l1 0x0000000000000000 0x0000000000000073
l2-l3 0x0000000000000000 0x0000000000000000
l4-l5 0x0000000000001000 0x0000000000000000
l6-l7 0x0000000000000000 0xffffffff7f3ede21
i0-i1 0x0000000100000e46 0xffffffff7ffff878
i2-i3 0xffffffff7f2a0de4 0xffffffff7fffefc9
i4-i5 0xffffffffffffffff 0x0000000100000e45
i6-i7 0xffffffff7fffef41 0xffffffff7f2a4de0
y 0x0000000000000000
ccr 0x0000000000000000
pc 0xffffffff7f239958
:strlen+0x18 ldub [%o2], %o1
npc 0xffffffff7f23995c
:strlen+0x1c tst %o1


Note:
You might have noticed that the value of global register, g0, is hard coded to 0. The primary reason being the first global register (g0 on SPARC) is the assembly language equivalent of /dev/null. No matter what you try to put into this register, the value always remain zero.

To DO://
  1. Enhance the above code to include input, local, floating-point registers. Also print the actual instructions of program counter (PC) and next program counter (nPC)
  2. Post similar code for x86/x64 architecture

__________
Technorati tags:
| |

No comments:

Post a Comment