























#ifdef CALLGRAPH_PROFILE
#include <stdio.h>
#include <stdlib.h>
#ifndef WIN32
#include <unistd.h>
#endif
#include <string.h>
#include <malloc.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "bsp_callstack.h"
#include "bsp_lib.h"
#ifdef OSF1
#include <excpt.h>
#endif /* OSF1 */
#if defined(SGI) || defined(SGIN32) ||defined(SGICHALL64) || defined(SGIORIGIN)
#include <sys/signal.h>
#include <exception.h>
#endif /* SGI */












































#if (defined(sparc) || defined(__sparc)) && \
    (defined(SunOS) || defined(Solaris))
#define CS_sparc
#endif   /* SunOS on sparc */

#if (defined(i386) || defined(__i386)) && \
    (defined(SunOS) || defined(SolarisX86) || defined(LINUX)  )
#define CS_i386
#endif /* SunOS or LINUX on Intel PC */


















#ifdef CS_sparc
extern int cs_regsp();

#define CS_ofs_pc 0x3c
#define CS_ofs_fp 0x38
#define CS_start "start"
#define CS_CAPTURE_CONTEXT() sp=cs_regsp()
#endif /* CS_sparc */

#ifdef SP2
extern void cs_regsp(unsigned int *sp);

#define CS_ofs_pc 8
#define CS_ofs_fp 0
#define CS_start "start"
#define CS_CAPTURE_CONTEXT() do {   \
    cs_regsp(&sp);                  \
    sp=*(int *)(sp+CS_ofs_fp);      \
  } while (0)
 /* regsp returns its sp */
#endif /* SP2 */

#ifdef CS_i386
extern long int cs_regsp();

#define CS_ofs_pc 4
#define CS_ofs_fp 0
#define CS_start "start"

#define CS_CAPTURE_CONTEXT() sp=cs_regsp()
#endif /* CS_i386 */

#ifdef WIN32
long int cs_regsp() {
	__asm {
        	mov eax,ebp
	}
}
#define CS_ofs_pc 4
#define CS_ofs_fp 0
#define CS_start "mainCRTStartup"

#define CS_CAPTURE_CONTEXT() sp=cs_regsp()
#endif /* WIN32 */


#if defined(CS_sparc) || defined(SP2) || defined(CS_i386) || defined(WIN32)
#define CS_VARIABLES unsigned int sp,*spc,*sfp
#define CS_FIRST_UNWIND()  do{                               \
    sfp=(unsigned int *)(sp+CS_ofs_fp);   /* old fp */       \
    spc=(unsigned int *)(sp+CS_ofs_pc);   /* old pc */       \
  } while (0)
#define CS_UNWIND()        do {                              \
    sp=*sfp;                                                 \
    sfp=(unsigned int *)(sp+CS_ofs_fp);   /* old fp */       \
    spc=(unsigned int *)(sp+CS_ofs_pc);   /* old pc */       \
  } while (0)
#define CS_CHECK_CONTEXT (((unsigned int)sfp)>1000)
#define CS_CONTEXT_PC *spc
#define CS_NO_NAMES 1
#endif









#ifdef CRAYMPP
extern cs_regfp();

#define CS_ofs_pc -8             /* not used but here for clarity */
#define CS_ofs_fp -16
#define CS_ofs_ssib -32
#define CS_ofs_name 40
#define CS_start "$START$"
#define CS_VARIABLES unsigned long int fp, ssib
#define CS_CAPTURE_CONTEXT() fp=cs_regfp()
#define CS_CHECK_CONTEXT fp
#define CS_UNWIND_NAME()  do {                                         \
     fp=*(int *)(fp+CS_ofs_fp);         /* old fp */                   \
     if (fp) ssib=* (unsigned int *)(fp + CS_ofs_ssib);                \
  } while (0)
     /* SSIB= Static Subprogram Information Block; The name of the
                                           subroutine is kept there.  */
#define CS_FIRST_UNWIND_NAME() CS_UNWIND_NAME()
#define CS_NAME (char *)(ssib+CS_ofs_name)
#endif /* CRAYMPP */















#if !defined(SGIORIGIN) && defined(CRAYPVP)
extern cs_regfp();
                 /* don't know where old pc is kept, but don't need it */
#define CS_ofs_fp 3                  /* offsets here are 8-byte words */
#define CS_ofs_nameptr 0
#define CS_start "$START$"
#define CS_VARIABLES unsigned int fp, nameptr, size;                    \
                     char name[TRUNCATE_CALLSTACK_FUNNAME]
#define CS_CAPTURE_CONTEXT() fp=cs_regfp()
#define CS_CHECK_CONTEXT fp
#define CS_UNWIND_NAME() do {                                           \
     fp=*(int *)(fp+CS_ofs_fp);      /* old fp */                       \
     if (fp) {                                                          \
          nameptr = *(unsigned int *)(fp+CS_ofs_nameptr);               \
          size    = *(unsigned int *)name % 0x100;                      \
          nameptr-= size / 8 + ((size % 8>0)?1:0);                      \
          if (size>TRUNCATE_CALLSTACK_FUNNAME)                          \
             size=TRUNCATE_CALLSTACK_FUNNAME;                           \
          else name[size-1]="\0";                                       \
       }                                                                \
   } while (0)
 /* \0 in name will be needed later, when it has been initialized      */
 /* divide by 8 since the address on this machine are in 8-byte words; */
#define CS_FIRST_UNWIND_NAME() CS_UNWIND_NAME()
#define CS_NAME strncpy(name,(char *) nameptr, size)

#endif /* CRAYPVP */












#ifdef OSF1 
#define CS_start "start"
#define CS_VARIABLES CONTEXT context
#define CS_CAPTURE_CONTEXT() exc_capture_context(&context)
#define CS_FIRST_UNWIND() exc_virtual_unwind(0,&context);
#define CS_UNWIND() CS_FIRST_UNWIND()
#define CS_CHECK_CONTEXT context.sc_pc
#define CS_CONTEXT_PC CS_CHECK_CONTEXT
#define CS_NO_NAMES 1
#endif /* OSF1 */











#if defined(SGICHALL64) || defined(SGI) || defined(SGIN32) ||defined(SGIORIGIN)

#define CS_VARIABLES          \
   char *name;                \
   struct sigcontext context
#define CS_CAPTURE_CONTEXT() exc_setjmp(&context)
#define CS_CHECK_CONTEXT context.sc_pc

#if (_MIPS_SIM == _MIPS_SIM_ABI32)
#define CS_UNWIND() unwind(&context,0)
#else /* _MIPS_SIM == _MIPS_SIM_ABI64 || _MIPS_SIM_NABI32*/
#define CS_UNWIND() exc_unwind(&context,0)
#endif /* if o32 MIPS */
#define CS_FIRST_UNWIND() CS_UNWIND()
#define CS_start "start"
#define CS_NO_NAMES 1
#define CS_CONTEXT_PC CS_CHECK_CONTEXT
#endif /* SGI */









#if defined(CALLGRAPH_PROFILE) && defined(CS_NO_NAMES)

static int last_symb;
static int globldiff=0;

struct t_symb {                              /* symbol table */
  char name[TRUNCATE_CALLSTACK_FUNNAME];
  unsigned long int offset;                       
} *symb;
#endif











void bsp_get_callstack(char funcs[TRUNCATE_CALLSTACK_DEPTH]
                                 [TRUNCATE_CALLSTACK_FUNNAME]) {
#ifdef CALLGRAPH_PROFILE
  unsigned int j; /* counter */
#ifdef CS_NO_NAMES

  long int i,k;  /* variables for the binary search */
  CS_VARIABLES;
  CS_CAPTURE_CONTEXT();    /* The first function is the current and is not returned. */
  CS_FIRST_UNWIND();

  j=0; 
  while ((j<TRUNCATE_CALLSTACK_DEPTH) && (CS_CHECK_CONTEXT) && 
         (j?(strcmp(funcs[j-1],CS_start) && strcmp(funcs[j-1],"main")):1)) {

      i=last_symb / 2 + last_symb % 2;k=i;            /* Binary Search */
      while ((k>1) && (i>=0)) {
	k=k / 2 + k % 2;
	if (CS_CONTEXT_PC>symb[i].offset+globldiff) i+=k; else i-=k;
      }
      /* maybe not necessary */ 
      while ((i>=0)&&(CS_CONTEXT_PC<=symb[i].offset+globldiff)) i--;

     if ((i<last_symb) || (i<0))
	strcpy(funcs[j],symb[i].name);
     else strcpy(funcs[j],CS_start);   /* The function isn't in the program */
     j++;
     CS_UNWIND();
  }

#else
  
  CS_VARIABLES;
  CS_CAPTURE_CONTEXT();
           /* The first function is the current and is not returned. */
  CS_FIRST_UNWIND_NAME();

  j=0;
  while ((j<TRUNCATE_CALLSTACK_DEPTH) && (CS_CHECK_CONTEXT) &&
         (j?strcmp(funcs[j-1],CS_start):1)) { 
       strncpy(funcs[j],CS_NAME,TRUNCATE_CALLSTACK_FUNNAME-1);
       funcs[j][TRUNCATE_CALLSTACK_FUNNAME-1]='\0';
       CS_UNWIND_NAME();
       j++;
  }   
#endif /* CS_NO_NAMES */

  /* Removes "start" function, which is always there before "main" */
  if (!strcmp(funcs[j-1],CS_start))
    funcs[j-1][0]='\0';
  else funcs[j][0]='\0';
#endif
}













#if defined(CALLGRAPH_PROFILE) && defined(CS_NO_NAMES)

static FILE *file;
/* Used in system quick sort */
static int t_symb_comp(struct t_symb *i,struct t_symb *j) {
   return((*i).offset-(*j).offset);
}
#define StrLength 160

#endif

void bsp_init_callstack() {
#if defined(CALLGRAPH_PROFILE) && defined(CS_NO_NAMES)
  char temp[StrLength],temp1[StrLength], filename[L_tmpnam],
       thisfunc[20]="bsp_init_callstack";
  char ch, *env;
  int CURM_symb=SYMBOL_TABLE_INC_SIZE;
  FILE *fptr;
  struct stat buf;
  time_t time1;

  int i,j;

  if (!(long int)(symb=(struct t_symb *) CALLOC(SYMBOL_TABLE_INC_SIZE,
						SIZEOF(struct t_symb)) ))
    bsp_abort("{bsp_init_callstack} Not enough memory.");

  if (stat(_bsp_exec_file,&buf)) {   /* file doesn't exist or cannot be read */
    env = getenv("BSP_EXEC");       /* try different file name */
    if (env) {                      /* set by bsprun */
      _bsp_exec_file=MALLOC(SIZEOF(env+1));
      strcpy(_bsp_exec_file,env);
      if (stat(_bsp_exec_file,&buf))
        bsp_abort("{bsp_begin}\n"
		  "\tCall-graph profiler can not access executable.\n"
		  "\tError with bsprun(1)");
    } else {
      bsp_abort("{bsp_begin}\n"
                "\tCall-graph profiler can not access executable.\n"
                "\tPlease run program with bsprun(1)");
    }
  }
  time1=buf.st_mtime;
  strcpy(filename,_bsp_exec_file);
  strcat(filename,".tab");
  if ((stat(filename,&buf))?1:(buf.st_mtime<time1)) { /* file with symbol
                            information doesn't exist or is not up to date */ 
    sprintf(temp,"bspnm -o %s %s",filename,_bsp_exec_file);
    if (system(temp)) bsp_abort("{bsp_init_callstack} bspnm had trouble");
   }    
 
  file=fopen(filename,"r");
  if (file==NULL)
     bsp_abort("{bsp_init_callstack} unable to open \"%s\"",filename);

  i=0;
  while (fgets(temp,StrLength,file)) {
    if (!sscanf(temp,"%ld %s",&symb[i].offset,temp1))
      bsp_abort("{bsp_init_callstack} incorrect format in output from bspnm");
    strncpy(symb[i].name,temp1,TRUNCATE_CALLSTACK_FUNNAME-1);
    symb[i].name[TRUNCATE_CALLSTACK_FUNNAME-1]='\0';
	if (i+1>=CURM_symb)
	  if (! (long int)(symb=(struct t_symb *)realloc(symb,
		 (CURM_symb+=SYMBOL_TABLE_INC_SIZE)*SIZEOF(struct t_symb) ))){
	    bsp_abort("bsp_init_callstack:Not enough memory. Aborting...");
	  }
	if (!strcmp(symb[i].name,thisfunc))  
          globldiff=((long int)bsp_init_callstack-symb[i].offset);
	i++;
       }    /* while (fgets ...) */
    last_symb=i-1;
    fclose(file);

    qsort((char *)symb,last_symb+1,SIZEOF(struct t_symb),
          (int(*)(const void*,const void*)) t_symb_comp);
/* Sorts the address to make binary search later. Although the input   */
/* might be already sorted, does this because sometimes there are      */
/* inconsistency of the options of nm. If the input is already sorted, */
/* this doesn't take long. Consider sorting in bspnm(1) and removing this. */

#endif
}
#endif

