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

Reply via email to