Hi,
I spent lot of time on debugging the code. am not sure about the
libunwind internals .. but libunwind failed on to extract frame information
from pthreads lib api (example pthread_cond_wait in this function the frame
pointer is getting changed to some value like 0x1). Libunwind works with
the examples provided by u .......
>From GDB I figured out more than esp or ebp or CFI they r doing lot more
stuffs to get stacktrace of a process like Prologue Analysis.
http://sources.redhat.com/gdb/onlinedocs/gdbint.html#Algorithms
Intially I implemented stack gen code using ptrace + esp + ebp it didnt
work and i tried with libwindi my bad luck libunwind also didnt work
32-bit machine
Version details
--------------------
[root@Shash PROJECT]# ls -l /usr/lib/libunwind*
lrwxrwxrwx. 1 root root 16 Feb 14 21:30 /usr/lib/libunwind-generic.so ->
libunwind-x86.so
lrwxrwxrwx. 1 root root 18 Feb 14 21:30 /usr/lib/libunwind.so ->
libunwind.so.7.0.0
lrwxrwxrwx. 1 root root 18 Feb 14 21:28 /usr/lib/libunwind.so.7 ->
libunwind.so.7.0.0
-rwxr-xr-x. 1 root root 42716 Dec 4 2009 /usr/lib/libunwind.so.7.0.0
lrwxrwxrwx. 1 root root 22 Feb 14 21:30 /usr/lib/libunwind-x86.so ->
libunwind-x86.so.7.0.0
lrwxrwxrwx. 1 root root 22 Feb 14 21:28 /usr/lib/libunwind-x86.so.7 ->
libunwind-x86.so.7.0.0
-rwxr-xr-x. 1 root root 63204 Dec 4 2009 /usr/lib/libunwind-x86.so.7.0.0
[root@Shash PROJECT]# gcc -v
Using built-in specs.
Target: i686-redhat-linux
Configured with: ../configure --prefix=/usr --mandir=/usr/share/man
--infodir=/usr/share/info
--with-bugurl=http://bugzilla.redhat.com/bugzilla--enable-bootstrap
--enable-shared --enable-threads=posix
--enable-checking=release --with-system-zlib --enable-__cxa_atexit
--disable-libunwind-exceptions --enable-gnu-unique-object
--enable-languages=c,c++,objc,obj-c++,java,fortran,ada
--enable-java-awt=gtk --disable-dssi
--with-java-home=/usr/lib/jvm/java-1.5.0-gcj-1.5.0.0/jre
--enable-libgcj-multifile --enable-java-maintainer-mode
--with-ecj-jar=/usr/share/java/eclipse-ecj.jar --disable-libjava-multilib
--with-ppl --with-cloog --with-tune=generic --with-arch=i686
--build=i686-redhat-linux
Thread model: posix
gcc version 4.4.5 20101112 (Red Hat 4.4.5-2) (GCC)
/lib/libc-2.12.2.so
/lib/libpthread-2.12.2.so
[root@Shash PROJECT]# cat /etc/redhat-release
Fedora release 13 (Goddard)
I attached two .c files one example program (pthread.c) for testing the
libunwind functionality and bractrace_libunwind.c using unwind lib which
unwinds the stack frame
How I compiled
------------------------
gcc backtrace_libunwind.c -lunwind-generic -lunwind-ptrace -g -o backtrace
gcc pthread.c -lpthread
Output
-------------
[root@Shash PROJECT]#
[root@Shash PROJECT]# pstack 4187
Thread 4 (Thread 0xb7848b70 (LWP 4188)):
#0 0x001d1416 in __kernel_vsyscall ()
#1 0x4cf3d22c in pthread_cond_wait@@GLIBC_2.3.2 () from
/lib/libpthread.so.0
#2 0x08048923 in watch_count ()
#3 0x4cf39919 in start_thread () from /lib/libpthread.so.0
#4 0x4ce7bd4e in clone () from /lib/libc.so.6
Thread 3 (Thread 0xb6e47b70 (LWP 4189)):
#0 0x001d1416 in __kernel_vsyscall ()
#1 0x4ce3bea6 in nanosleep () from /lib/libc.so.6
#2 0x4ce3bcd0 in sleep () from /lib/libc.so.6
#3 0x080488ab in inc_count ()
#4 0x4cf39919 in start_thread () from /lib/libpthread.so.0
#5 0x4ce7bd4e in clone () from /lib/libc.so.6
Thread 2 (Thread 0xb6446b70 (LWP 4190)):
#0 0x001d1416 in __kernel_vsyscall ()
#1 0x4ce3bea6 in nanosleep () from /lib/libc.so.6
#2 0x4ce3bcd0 in sleep () from /lib/libc.so.6
#3 0x080488ab in inc_count ()
#4 0x4cf39919 in start_thread () from /lib/libpthread.so.0
#5 0x4ce7bd4e in clone () from /lib/libc.so.6
Thread 1 (Thread 0xb78496c0 (LWP 4187)):
#0 0x001d1416 in __kernel_vsyscall ()
#1 0x4cf39fdd in pthread_join () from /lib/libpthread.so.0
#2 0x08048a77 in main ()
[root@Shash PROJECT]# vim backtrace_libunwind.c
[root@Shash PROJECT]# gcc backtrace_libunwind.c ^C
(reverse-i-search)`': vim backtrace_^Cbunwind.c
[root@Shash PROJECT]# gcc backtrace_libunwind.c -lunwind-generic
-lunwind-ptrace -g -o backtrace
[root@Shash PROJECT]# ./a.out ^C
[root@Shash PROJECT]# ./backtrace 4187
----------tid4187----------
main
__libc_start_main
_start
----------tid4188----------
----------tid4189----------
inc_count
start_thread
clone
----------tid4190----------
inc_count
start_thread
clone
On Tue, Feb 21, 2012 at 9:50 PM, Arun Sharma <[email protected]> wrote:
> On Mon, Feb 20, 2012 at 2:40 AM, Sasikanth <[email protected]>
> wrote:
> > Hi all,
> >
> > I am trying to unwind stack frame using lib unwind lib, but most of the
> > time i could not able to get the stack trace, especially t system call
> > frames and pthread lib frame ( am not sure about the other lib frames).
> > A little debugging inside the code i figured out libwind failes where
> the
> > base pointer getting changed for eg pthread_cond_wait changes the bp and
> > copies it into some other register
>
> Can you provide the version of glibc and libunwind? In theory, this
> should work just fine on x64:
>
> http://bit.ly/w5IrVK
>
> Since gdb is able to unwind, I'm guessing that the dwarf unwind info
> is present and libunwind should be able to use it too.
>
> -Arun
>
/******************************************************************************
* * FILE: condvar.c
* * DESCRIPTION:
* * Example code for using Pthreads condition variables. The main thread
* * creates three threads. Two of those threads increment a "count" variable,
* * while the third thread watches the value of "count". When "count"
* * reaches a predefined limit, the waiting thread is signaled by one of the
* * incrementing threads. The waiting thread "awakens" and then modifies
* * count. The program continues until the incrementing threads reach
* * TCOUNT. The main program prints the final value of count.
* * SOURCE: Adapted from example code in "Pthreads Programming", B. Nichols
* * et al. O'Reilly and Associates.
* * LAST REVISED: 07/16/09 Blaise Barney
* ******************************************************************************/
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#define NUM_THREADS 3
#define TCOUNT 10
#define COUNT_LIMIT 12
int count = 0;
pthread_mutex_t count_mutex;
pthread_cond_t count_threshold_cv;
void *inc_count(void *t)
{
int i;
long my_id = (long)t;
for (i=0; i < TCOUNT; i++) {
pthread_mutex_lock(&count_mutex);
count++;
/*
* Check the value of count and signal waiting thread when condition is
* reached. Note that this occurs while mutex is locked.
* */
if (count == COUNT_LIMIT) {
printf("inc_count(): thread %ld, count = %d Threshold reached. ",
my_id, count);
pthread_cond_signal(&count_threshold_cv);
printf("Just sent signal.\n");
}
printf("inc_count(): thread %ld, count = %d, unlocking mutex\n",
my_id, count);
pthread_mutex_unlock(&count_mutex);
sleep(1000);
}
pthread_exit(NULL);
}
void *watch_count(void *t)
{
long my_id = (long)t;
printf("Starting watch_count(): thread %ld\n", my_id);
/*
* Lock mutex and wait for signal. Note that the pthread_cond_wait routine
* will automatically and atomically unlock mutex while it waits.
* Also, note that if COUNT_LIMIT is reached before this routine is run by
* the waiting thread, the loop will be skipped to prevent pthread_cond_wait
* from never returning.
* */
pthread_mutex_lock(&count_mutex);
if (count < COUNT_LIMIT) {
printf("watch_count(): thread %ld going into wait...\n", my_id);
pthread_cond_wait(&count_threshold_cv, &count_mutex);
printf("watch_count(): thread %ld Condition signal received.\n", my_id);
count += 125;
printf("watch_count(): thread %ld count now = %d.\n", my_id, count);
}
pthread_mutex_unlock(&count_mutex);
pthread_exit(NULL);
}
int main(int argc, char *argv[])
{
int i, rc;
long t1=1, t2=2, t3=3;
pthread_t threads[3];
pthread_attr_t attr;
/* Initialize mutex and condition variable objects */
pthread_mutex_init(&count_mutex, NULL);
pthread_cond_init (&count_threshold_cv, NULL);
/* For portability, explicitly create threads in a joinable state */
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
pthread_create(&threads[0], &attr, watch_count, (void *)t1);
pthread_create(&threads[1], &attr, inc_count, (void *)t2);
pthread_create(&threads[2], &attr, inc_count, (void *)t3);
/* Wait for all threads to complete */
for (i = 0; i < NUM_THREADS; i++) {
pthread_join(threads[i], NULL);
}
printf ("Main(): Waited on %d threads. Final value of count = %d. Done.\n",
NUM_THREADS, count);
/* Clean up and exit */
pthread_attr_destroy(&attr);
pthread_mutex_destroy(&count_mutex);
pthread_cond_destroy(&count_threshold_cv);
pthread_exit (NULL);
}
#include <stdio.h>
#include <sys/ptrace.h>
#include <sys/ptrace.h>
#include <asm/ptrace.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/user.h>
#include <fcntl.h>
#include <link.h>
#include <malloc.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <limits.h>
#include <stdlib.h>
#include <sys/types.h>
#include <libunwind-ptrace.h>
#include <limits.h>
#include <dirent.h>
#include <bfd.h>
#include <libunwind.h>
#include <unwind.h>
#define false 0
static int verbose_option = 0;
static int debug_option = 0;
static int wait_loops = 20;
static int wait_time = 100;
static int pointer_size = 8;
typedef Elf64_Addr TARGET_ADDRESS;
typedef struct _process_info {
int pid;
int threads_present_flag;
long *thread_pids;
} process_info;
static void msleep(int msecs)
{
usleep(msecs*1000);
}
static int attach_target(int thepid)
{
int ret;
int waitstatus;
int x;
if (debug_option) printf("Attaching to the target process...\n");
ret = ptrace(PTRACE_ATTACH, thepid, NULL, NULL);
if (0 != ret && 0 != errno) {
ret = errno;
return ret;
}
/* ptrace(PTRACE_ATTACH) does the equivalent of sending a SIG_STOP to the target.
So we should wait for that signal to be handled before proceeding.
*/
if (debug_option) printf("Waiting for target process to stop...\n");
x = 0;
while (x < wait_loops) {
ret = waitpid(thepid, &waitstatus, WUNTRACED | WNOHANG);
if (debug_option) {
printf("waitpid after attach returned: %d, status=%d\n",ret, waitstatus);
}
if (WIFSTOPPED(waitstatus)) {
return 0;
}
msleep(wait_time); /* Sleep for a bit so we don't busy wait */
x++;
}
if (debug_option) printf("Target process has stopped.\n");
/* If we did attach, install a signal handler to allow us to detatch if we're interrupted */
if (0 == errno) {
/* Try to catch the following signals:
SIGINT, SIGSEV,
*/
}
return errno;
}
static int attach_thread(long threadpid)
{
int ret;
int waitstatus;
if (debug_option) printf("Attaching to the target thread %ld...\n", threadpid);
ret = ptrace(PTRACE_ATTACH, threadpid, NULL, NULL);
if (0 != ret && 0 != errno) {
perror("ptrace(PTRACE_ATTACH)");
return errno;
}
while (1) {
ret = waitpid(threadpid, &waitstatus, __WCLONE);
if (ret > 0) {
break;
}
}
return errno;
}
static int detatch_target(process_info *pi)
{
int ret;
if (pi->threads_present_flag) {
int thread_pid = 0;
int x = 0;
if (debug_option) printf("Detatching from threads...\n");
for (x = 1; (pi->thread_pids)[x];x++) {
thread_pid = (pi->thread_pids)[x];
if (debug_option) printf("Detatching from thread %d\n", thread_pid);
ret = ptrace(PTRACE_CONT, thread_pid, 1, 0);
if (debug_option) printf("ptrace(PTRACE_CONT) returned: %d\n", ret);
}
}
if (debug_option) printf("Detaching from target...\n");
ret = ptrace(PTRACE_CONT, pi->pid, 1, 0);
if (debug_option) printf("ptrace64(PTRACE_CONT) returned: %d\n", ret);
return ret;
}
process_info *pi_alloc(int pid)
{
process_info* ret = (process_info*)calloc(sizeof(process_info),1);
if (NULL != ret) {
ret->pid = pid;
}
return ret;
}
void pi_free(process_info *pi)
{
free(pi);
}
int grok_and_print_thread_stack(process_info *pi, int thepid)
{
return unwind_thread_callstack(thepid);
}
int unwind_thread_callstack(int thetid)
{
unw_cursor_t c;
unw_word_t ip;
unw_addr_space_t as;
struct UPT_info *ui;
char buf[512];
int ret;
pid_t pid;
as = unw_create_addr_space(&_UPT_accessors,0);
ui = _UPT_create(thetid);
unw_init_remote(&c,as,ui);
do {
unw_get_proc_name(&c,buf,sizeof(buf),NULL);
printf("%s\n",buf);
}
while((ret = unw_step(&c)) > 0);
_UPT_destroy(ui);
ptrace(PTRACE_DETACH,thetid,0,0);
}
int grok_get_threads(process_info *pi)
{
int threads_present=0; int loop=0,numofth=0,tid=0;
char pids[16];
long thread_pids[10000];//handles 10,000 threads . TO DO have a linked list here instead of array
struct dirent *tids;
DIR *dp;
char *format_string = "/proc/%d/task";
char *tasksdir = calloc(strlen(format_string) + 10 ,1);
sprintf(tasksdir,format_string,pi->pid);
dp=opendir(tasksdir);
if(dp==NULL){
perror("cant open /proc/pid/task dir");
}
while (tids=readdir(dp))
{
if(strcmp(tids->d_name, " ")!=0 && strlen(tids->d_name) > 0 )
{
if(debug_option)
printf("\n loop i s%d",loop);
strcpy(pids,tids->d_name);
pids[strlen(tids->d_name)]='\0';
if(strcmp(pids,".") !=0 && strcmp(pids,"..") !=0 && strlen(pids) > 1 ){
thread_pids[loop]=atol(pids);
if(thread_pids[loop] != pi->pid )
attach_thread(thread_pids[loop]);
if(debug_option)
printf("\n tid is %ld", thread_pids[loop]);
loop++;
}
}
}
thread_pids[loop]='\0';
closedir(dp);
if(loop > 1){
pi->threads_present_flag = 1;
}
pi->thread_pids=thread_pids ;
for(tid=0;(pi->thread_pids)[tid];tid++){
printf("\n----------tid%ld----------\n",pi->thread_pids[tid]);
unwind_thread_callstack(pi->thread_pids[tid]);
}
}
static void fatal(char* s)
{
fprintf(stderr,"vstack: fatal error: %s\n",s);
exit(0);
}
static void usage()
{
printf("vstack: [-v] [-D] \n");
exit(1);
}
int main(int argc, char** argv)
{
/* look for command line options */
int pid = 0;
int ret = 0;
process_info *pi = NULL;
int option_position = 1;
while ( option_position < (argc-1) && *argv[option_position] == '-') {
switch (*(argv[option_position]+1)) {
case 'v':
verbose_option = 1;
break;
case 'D':
debug_option = 1;
break;
default:
usage();
break;
}
option_position++;
}
if (option_position != (argc-1) ) {
usage();
}
pid = atoi(argv[option_position]);
if (0 == pid) {
usage();
}
if (debug_option) {
printf("verbose option: %s\n",verbose_option?"on":"off");
printf("pid: %d\n",pid);
}
/* check that the pesky user hasn't tried to lsstack himself */
if (pid == getpid() ) {
fprintf(stderr,"Error: specified pid belongs to the lsstack process\n");
exit(1);
}
/* See if we can attach to the target */
ret = attach_target(pid);
if (ret) {
fprintf(stderr,"Failed to attach to the target process: %s\n", strerror(ret) );
exit(1);
}
if (debug_option) printf("Attached to target process\n");
pi = pi_alloc(pid);
// get the tids from /proc/pid/tasks for Linux64 . the tasks dir has entries for each threadid
ret=grok_get_threads(pi);
if (!(pi->threads_present_flag))
ret=grok_and_print_thread_stack(pi, pi->pid) ;
// detach target to continue itself , otherwise process will be in suspend state
detatch_target(pi);
pi_free(pi);
if (debug_option) printf("Detatched from target process\n");
return 0;
}
_______________________________________________
Libunwind-devel mailing list
[email protected]
https://lists.nongnu.org/mailman/listinfo/libunwind-devel