/* * $Id$ * * :ts=4 * * Portable ISO 'C' (1994) runtime library for the Amiga computer * Copyright (c) 2002-2015 by Olaf Barthel * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Neither the name of Olaf Barthel nor the names of contributors * may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #define SCALE_1_TO_1 0x10000L #include "profile_gmon.h" #undef DebugPrintF #define dprintf(format, args...)((struct ExecIFace *)((*(struct ExecBase **)4)->MainInterface))->DebugPrintF("[%s] " format, __PRETTY_FUNCTION__ , ## args) struct gmonparam _gmonparam = { state: kGmonProfOn }; static unsigned int s_scale; void moncontrol(int); void monstartup(uint32, uint32); void moncleanup(void); void mongetpcs(uint32* lowpc, uint32 *highpc); extern int profil(uint16 *buffer, uint32 bufSize, uint32 offset, uint32 scale); void monstartup(uint32 low_pc, uint32 high_pc) { uint8 *cp; uint32 lowpc, highpc; struct gmonparam *p = &_gmonparam; dprintf("in monstartup)\n"); /* * If we don't get proper lowpc and highpc, then * we'll try to get them from the elf handle. */ if (low_pc == 0 && high_pc == 0) { mongetpcs(&lowpc, &highpc); } else { lowpc = low_pc; highpc = high_pc; } /* * Round lowpc and highpc to multiples of the density * to prevent using floating point scaling */ p->lowpc = ROUNDDOWN(lowpc, HISTFRACTION * sizeof(HISTCOUNTER)); p->highpc = ROUNDUP(highpc, HISTFRACTION * sizeof(HISTCOUNTER)); /* Size of the text segment */ p->textsize = p->highpc - p->lowpc; /* * Size of the histogram. Due to the nature of PowerPC code, * we can safely use a histogram fraction of at least 4, since * every instruction is exactly one word wide and always aligned. */ p->kcountsize = p->textsize / HISTFRACTION; /* * The hash table size */ p->hashfraction = HASHFRACTION; p->fromssize = p->textsize / p->hashfraction; p->tolimit = p->textsize * ARCDENSITY / 100; if (p->tolimit < MINARCS) p->tolimit = MINARCS; else if (p->tolimit > MAXARCS) p->tolimit = MAXARCS; p->tossize = p->tolimit * sizeof(struct tostruct); dprintf("lowpc = %p, highpc = %p\n", lowpc, highpc); dprintf("textsize = %d\n", p->textsize); dprintf("kcountsize = %d\n", p->kcountsize); dprintf("fromssize = %d\n", p->fromssize); dprintf("tolimit = %d, tossize = %d\n", p->tolimit, p->tossize); cp = (uint8*)AllocMem(p->kcountsize + p->fromssize + p->tossize, MEMF_CLEAR); if (!cp) { p->state = kGmonProfError; return; } p->memory = cp; p->tos = (struct tostruct *)cp; cp += p->tossize; p->kcount = (uint16 *)cp; cp += p->kcountsize; p->froms = (uint16 *)cp; p->tos[0].link = 0; /* Verify granularity for sampling */ if (p->kcountsize < p->textsize) /* FIXME Avoid floating point */ s_scale = ((float)p->kcountsize / p->textsize) * SCALE_1_TO_1; else s_scale = SCALE_1_TO_1; s_scale >>= 1; dprintf("Enabling monitor\n"); moncontrol(1); } void moncontrol(int mode) { struct gmonparam *p = &_gmonparam; if (mode) { /* Start profiling. */ profil((uint16 *)p->kcount, (size_t)p->kcountsize, p->lowpc, s_scale); p->state = kGmonProfOn; } else { /* Stop Profiling. */ profil(NULL, 0, (uint32)0, 0); p->state = kGmonProfOff; } } void moncleanup(void) { BPTR fd; int fromindex; int endfrom; uint32 frompc; int toindex; struct rawarc rawarc; struct gmonparam *p = &_gmonparam; struct gmonhdr gmonhdr, *hdr; #ifdef DEBUG FILE *log; #endif moncontrol(0); if (p->state == kGmonProfError) { fprintf(stderr, "WARNING: Overflow during profiling\n"); } fd = Open("gmon.out", MODE_NEWFILE); if (!fd) { fprintf(stderr, "ERROR: could not open gmon.out\n"); return; } hdr = (struct gmonhdr *)&gmonhdr; hdr->lpc = 0; //p->lowpc; hdr->hpc = p->highpc - p->lowpc; hdr->ncnt = (int)p->kcountsize + sizeof(gmonhdr); hdr->version = GMONVERSION; hdr->profrate = 100; //FIXME:!! Write(fd, hdr, sizeof(*hdr)); Write(fd, p->kcount, p->kcountsize); endfrom = p->fromssize / sizeof(*p->froms); #ifdef DEBUG log = fopen("gmon.log", "w"); #endif for (fromindex = 0; fromindex < endfrom; fromindex++) { if (p->froms[fromindex] == 0) continue; frompc = 0; /* FIXME: was p->lowpc; needs to be 0 and assumes -Ttext=0 on compile. Better idea? */ frompc += fromindex * p->hashfraction * sizeof (*p->froms); for (toindex = p->froms[fromindex]; toindex != 0; toindex = p->tos[toindex].link) { #ifdef DEBUG if (log) fprintf(log, "%p called from %p: %d times\n", frompc, p->tos[toindex].selfpc, p->tos[toindex].count); #endif rawarc.raw_frompc = frompc; rawarc.raw_selfpc = p->tos[toindex].selfpc; rawarc.raw_count = p->tos[toindex].count; Write(fd, &rawarc, sizeof(rawarc)); } } #ifdef DEBUG if (log) fclose(log); #endif Close(fd); } void mongetpcs(uint32* lowpc, uint32 *highpc) { struct Library *ElfBase = NULL; struct ElfIFace *IElf = NULL; struct Process *self; BPTR seglist; Elf32_Handle elfHandle; uint32 i; Elf32_Shdr *shdr; uint32 numSections; *lowpc = 0; *highpc = 0; ElfBase = OpenLibrary("elf.library", 0L); if (!ElfBase) goto out; IElf = (struct ElfIFace *)GetInterface(ElfBase, "main", 1, NULL); if (!IElf) goto out; self = (struct Process *)FindTask(0); seglist = GetProcSegList(self, GPSLF_CLI | GPSLF_SEG); GetSegListInfoTags(seglist, GSLI_ElfHandle, &elfHandle, TAG_DONE); elfHandle = OpenElfTags( OET_ElfHandle, elfHandle, TAG_DONE); if (!elfHandle) goto out; GetElfAttrsTags(elfHandle, EAT_NumSections, &numSections, TAG_DONE); for (i = 0; i < numSections; i++) { shdr = GetSectionHeaderTags(elfHandle, GST_SectionIndex, i, TAG_DONE); if (shdr && (shdr->sh_flags & SWF_EXECINSTR)) { uint32 base = (uint32)GetSectionTags(elfHandle, GST_SectionIndex, i, TAG_DONE); *lowpc = base; *highpc = base + shdr->sh_size; break; } } CloseElfTags(elfHandle, CET_ReClose, TRUE, TAG_DONE); out: if (IElf) DropInterface((struct Interface *)IElf); if (ElfBase) CloseLibrary(ElfBase); } #include "macros.h" int __profiler_init(void) __attribute__((constructor)); void __profiler_exit(void) __attribute__((destructor)); int __profiler_init(void) { monstartup(0,0); return OK; } void __profiler_exit(void) { moncleanup(); }