flycast/newdc/linux/nixprof.cpp

318 lines
6.9 KiB
C++

#include "types.h"
#include "cfg/cfg.h"
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
#include <memory.h>
#include <signal.h>
//#include <sys/ucontext.h>
#include <stdio.h>
#include <signal.h>
//#include <execinfo.h>
#include <sys/syscall.h>
#include <sys/stat.h>
#include <poll.h>
#include <termios.h>
//#include <curses.h>
#include <fcntl.h>
#include <semaphore.h>
#include <stdarg.h>
#include <signal.h>
#include <sys/param.h>
#include <sys/mman.h>
#include <sys/time.h>
#include "hw/sh4/dyna/blockmanager.h"
#include <set>
#include "deps/libelf/elf.h"
#if defined(_ANDROID)
#include <asm/sigcontext.h>
typedef struct ucontext_t {
unsigned long uc_flags;
struct ucontext_t *uc_link;
struct {
void *p;
int flags;
size_t size;
} sstack_data;
struct sigcontext uc_mcontext;
/* some 2.6.x kernel has fp data here after a few other fields
* we don't use them for now...
*/
} ucontext_t;
#include <android/log.h>
#endif
#if HOST_CPU == CPU_ARM
#define GET_PC_FROM_CONTEXT(c) (((ucontext_t *)(c))->uc_mcontext.arm_pc)
#else
#define GET_PC_FROM_CONTEXT(c) (((ucontext_t *)(c))->uc_mcontext.pc)
#endif
/**
@file CallStack_Android.h
@brief Getting the callstack under Android
@author Peter Holtwick
*/
#include <unwind.h>
#include <stdio.h>
#include <string.h>
int tick_count=0;
pthread_t proft;
pthread_t thread[2];
void* prof_address[2];
u32 prof_wait;
u8* syms_ptr;
int syms_len;
void sample_Syms(u8* data,u32 len)
{
syms_ptr=data;
syms_len=len;
}
void prof_handler (int sn, siginfo_t * si, void *ctxr)
{
ucontext_t* ctx=(ucontext_t*)ctxr;
int thd=-1;
if (pthread_self()==thread[0]) thd=0;
else if (pthread_self()==thread[1]) thd=1;
else return;
prof_address[thd]=(void*)GET_PC_FROM_CONTEXT(ctx);
}
void install_prof_handler(int id)
{
struct sigaction act, segv_oact;
memset(&act, 0, sizeof(act));
act.sa_sigaction = prof_handler;
sigemptyset(&act.sa_mask);
act.sa_flags = SA_SIGINFO | SA_RESTART;
sigaction(SIGPROF, &act, &segv_oact);
thread[id]=pthread_self();
}
void prof_head(FILE* out, const char* type, const char* name)
{
fprintf(out,"==xx==xx==\n%s:%s\n",type,name);
}
void prof_head(FILE* out, const char* type, int d)
{
fprintf(out,"==xx==xx==\n%s:%d\n",type,d);
}
void elf_syms(FILE* out,const char* libfile)
{
struct stat statbuf;
printf("LIBFILE \"%s\"\n",libfile);
int fd=open(libfile,O_RDONLY,0);
if (!fd)
{
printf("Failed to open file \"%s\"\n",libfile);
return;
}
if (fstat (fd,&statbuf) < 0)
{
printf("Failed to fstat file \"%s\"\n",libfile);
return;
}
{
void* data=mmap (0, statbuf.st_size, PROT_READ, MAP_SHARED, fd, 0);
close(fd);
if (data==(void*)-1)
{
printf("Failed to mmap file \"%s\"\n",libfile);
return;
}
//printf("MMap: %08p, %08X\n",data,statbuf.st_size);
int dynsym=-1;
int dynstr=-1;
/*
Section: 2 -> .dynsym
Section: 3 -> .dynstr
*/
if (elf_checkFile(data)>=0)
{
int scnt=elf_getNumSections(data);
for (int si=0;si<scnt;si++)
{
if (strcmp(".dynsym",elf_getSectionName(data,si))==0)
dynsym=si;
if (strcmp(".dynstr",elf_getSectionName(data,si))==0)
dynstr=si;
}
}
else
printf("invalid elf file\n");
if (dynsym >= 0)
{
prof_head(out,"libsym",libfile);
// printf("Found dymsym %d, and dynstr %d!\n",dynsym,dynstr);
elf_symbol* sym=(elf_symbol*)elf_getSection(data,dynsym);
int symcnt=elf_getSectionSize(data,dynsym)/sizeof(elf_symbol);
for (int i=0;i<symcnt;i++)
{
if (sym[i].st_value && sym[i].st_name && sym[i].st_shndx)
{
char* name=(char*)elf_getSection(data,dynstr);// sym[i].st_shndx
// printf("Symbol %d: %s, %08X, %d bytes\n",i,name+sym[i].st_name,sym[i].st_value,sym[i].st_size);
fprintf(out,"%08X %d %s\n",sym[i].st_value,sym[i].st_size,name+sym[i].st_name);
}
}
}
else
printf("no dynsym\n");
munmap(data,statbuf.st_size);
}
}
volatile bool prof_run;
int str_ends_with(const char * str, const char * suffix) {
if( str == NULL || suffix == NULL )
return 0;
size_t str_len = strlen(str);
size_t suffix_len = strlen(suffix);
if(suffix_len > str_len)
return 0;
return 0 == strncmp( str + str_len - suffix_len, suffix, suffix_len );
}
void sh4_jitsym(FILE* out);
void* prof( void *ptr )
{
FILE* prof_out;
char line[512];
sprintf(line,"/%d.reprof",tick_count);
string logfile=GetPath(line);
printf("Profiler thread logging to -> %s\n",logfile.c_str());
prof_out=fopen(logfile.c_str(),"wb");
if (!prof_out)
{
printf("Failed to open profiler file\n");
return 0;
}
set<string> libs;
prof_head(prof_out,"vaddr","");
FILE* maps=fopen("/proc/self/maps","r");
while(!feof(maps))
{
fgets(line,512,maps);
fputs(line,prof_out);
if(strstr(line,".so"))
{
char file[512];
file[0]=0;
sscanf(line,"%*x-%*x %*s %*x %*x:%*x %*d %s\n",file);
if (strlen(file))
libs.insert(file);
}
}
//Write map file
prof_head(prof_out,".map","");
fwrite(syms_ptr,1,syms_len,prof_out);
//write exports from .so's
for (set<string>::iterator it=libs.begin(); it!=libs.end(); it++)
{
elf_syms(prof_out,it->c_str());
}
//Write shrec syms file !
prof_head(prof_out,"jitsym","SH4");
sh4_jitsym(prof_out);
//Write arm7rec syms file ! -> to do
//prof_head(prof_out,"jitsym","ARM7");
prof_head(prof_out,"samples",prof_wait);
do
{
tick_count++;
//printf("Sending SIGPROF %08X %08X\n",thread[0],thread[1]);
for (int i=0;i<2;i++) pthread_kill(thread[i],SIGPROF);
//printf("Sent SIGPROF\n");
usleep(prof_wait);
//fwrite(&prof_address[0],1,sizeof(prof_address[0])*2,prof_out);
fprintf(prof_out,"%p %p\n",prof_address[0],prof_address[1]);
if (!(tick_count%10000))
{
printf("Profiler: %d ticks, flusing ..\n",tick_count);
fflush(prof_out);
}
} while(prof_run);
fclose(maps);
fclose(prof_out);
}
void sample_Start(int freq)
{
prof_wait=1000000/freq;
printf("sampling profiler: starting %d hz %d wait\n",freq,prof_wait);
prof_run=true;
pthread_create( &proft, NULL, prof, 0);
}
void sample_Stop()
{
if (prof_run)
{
prof_run=false;
pthread_join( proft, NULL);
}
printf("sampling profiler: stopped\n");
}