/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */ /* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include #include #include #include #include #include #include #include #include #include #include #include #include static void warn(const char *tmpl, ...) { va_list ap; va_start(ap, tmpl); vfprintf(stderr, tmpl, ap); fputs("\n", stderr); va_end(ap); } static void fatal(const char *tmpl, ...) { va_list ap; va_start(ap, tmpl); vfprintf(stderr, tmpl, ap); fputs("\n", stderr); va_end(ap); exit(-1); } struct Chunk { uint32_t tag; uint32_t size; char data[0]; }; struct DebugInfo { const Chunk *info; uint64_t pc() const { return ((uint64_t *) info->data)[0]; } uint32_t tag() const { return info->tag; } std::string file() const { if (tag() != 'file') fatal("DebugInfo::file() called on non-file debug info"); return std::string(info->data + sizeof(uint64_t), info->size - sizeof(uint64_t)); } uint64_t line() const { if (tag() != 'line' ) fatal("DebugInfo::line() called on non-linedebug info"); return ((uint64_t *) info->data)[1]; } bool operator<(const DebugInfo &other) const { int64_t pcPad = pc() - other.pc(); if (pcPad) return pcPad < 0; if (tag() == 'file' && other.tag() != 'file') return true; return false; } }; struct MethBlock { const Chunk *meth; const Chunk *block; const Chunk *code; uint64_t start() const { return ((uint64_t *) block->data)[0]; } uint64_t end() const { return ((uint64_t *) block->data)[1]; } std::string name() const { return std::string(meth->data, meth->size); } const void *codeBytes() const { return code ? code->data : NULL; } const uint32_t codeLen() const { return code ? code->size : 0; } bool operator<(const MethBlock &other) const { return start() < other.start(); } }; static std::string quote(const std::string &s) { #ifdef __MACH__ std::string result = "\""; std::string::const_iterator i = s.begin(), e = s.end(); for (; i != e; i++) { if (*i != '\\' && isprint(*i)) result += *i; else { char buf[8]; sprintf(buf, "\\%03o", *i); result += buf; } } result += "\""; return result; #else #error foo #endif } int main(int argc, char **argv) { if (argc != 2) fatal("usage: %s inputLog", argv[0]); int fd = open(argv[1], O_RDONLY); if (fd < 0) fatal("couldn't open %s", argv[1]); off_t len = lseek(fd, 0, SEEK_END); const void *cur = mmap(NULL, len, PROT_READ, MAP_PRIVATE, fd, 0); const void *end = (char *) cur + len; if (cur == MAP_FAILED) fatal("couldn't mmap %s", argv[1]); const Chunk *base = NULL; const Chunk *meth = NULL; std::vector blocks; std::vector debugInfos; int pad_count = 0; while (cur < end) { const Chunk &chunk = *(const Chunk *) cur; switch (chunk.tag) { default: warn("unknown tag %08x", chunk.tag); break; case 'base': base = &chunk; break; case 'meth': meth = &chunk; break; case 'blok': { if (!meth) fatal("'blok' tag without 'meth'"); MethBlock block; block.meth = meth; block.block = &chunk; block.code = NULL; blocks.push_back(block); } break; case 'code': { if (blocks.size() <= 0) fatal("'code' chunk without 'blok'"); blocks.back().code = &chunk; } break; case 'file': case 'line': { DebugInfo info = { &chunk }; debugInfos.push_back(info); } break; } cur = chunk.data + chunk.size; } printf(".text\n"); #ifdef __MACH__ printf(".align 12\n"); #else printf(".align 4096\n"); #endif printf(".globl __jitStart\n"); printf(".globl _jitStart\n"); printf("__jitStart:\n"); printf("_jitStart:\n"); printf(".align 0\n"); std::sort(blocks.begin(), blocks.end()); std::sort(debugInfos.begin(), debugInfos.end()); std::map namesSeen; std::vector::const_iterator i, e; std::vector::const_iterator di, de; if (!base) fatal("no 'base' found"); uint64_t textOffs = 0x1000; uint64_t textLen = 256 * 1024 * 1024; if (base->size <= sizeof(uint64_t)) fatal("base info too short"); char path[4096]; int path_len = base->size - sizeof(uint64_t); if (path_len >= sizeof(path)) fatal("path to base library too long"); memcpy(path, base->data + sizeof(uint64_t), path_len); path[path_len] = 0; void *hdl = dlopen(path, RTLD_NOW); if (!hdl) fatal("couldn't get info from base library %s", path); char *jit_start = (char*) dlsym(hdl, "_jitStart"); if (!jit_start) fatal("couldn't find _jitStart in base library"); Dl_info dlInfo; if (dladdr(jit_start, &dlInfo)) { if ((jit_start - (char *) dlInfo.dli_fbase) != textOffs) warn("_jitStart at unexpected location"); } else { warn("couldn't validate _jitStart location"); } char *jit_end = (char*) dlsym(hdl, "_jitEnd"); if (!jit_end) fatal("couldn't find _jitEnd in base library"); textLen = jit_end - jit_start; dlclose(hdl); uint64_t curAddr = *(uint64_t *) base->data + textOffs; uint64_t endAddr = curAddr + textLen; int dbgFile = 0; di = debugInfos.begin(); de = debugInfos.end(); for (i = blocks.begin(), e = blocks.end(); i != e; ++i) { int curDbgFile = 0; uint64_t block_start = i->start(); //### uint64_t block_end = i->end(); //### std::string name = i->name(); int seen = namesSeen[name]++; if (seen) { char buf[16]; sprintf(buf, " (%d)", seen); name += buf; } uint64_t pad = block_start - curAddr; if (pad) { printf("pad%d:\n", pad_count++); printf(".fill %lld,1,0xcc\n", pad); } curAddr += pad; while (di != de && di->pc() < curAddr) { warn("dropped debug info at %llx", di->pc()); di++; } printf("%s:\n", quote(name).c_str()); const void *code = i->codeBytes(); if (code) { uint32_t codeLen = i->codeLen(); if (codeLen != block_end - block_start) { printf("FAIL\n"); } while (codeLen) { while (di != de && di->pc() == curAddr) { switch (di->tag()) { default: warn("unknown debug tag %08x", di->tag()); break; case 'file': printf(".file %d %s\n", curDbgFile = ++dbgFile, quote(di->file()).c_str()); break; case 'line': if (curDbgFile) printf(".loc %d %lld 0\n", dbgFile, di->line()); break; } di++; } printf(".byte "); int nextDebug = (di == de) ? codeLen : di->pc() - curAddr; int nextDebugHere = (nextDebug > codeLen) ? codeLen : nextDebug; int atOnce = (nextDebugHere < 12) ? nextDebugHere : 12; while (atOnce--) { printf("0x%02x%s", *(const unsigned char *) code, atOnce ? ", " : "\n"); code = (const char *) code + 1; codeLen--; curAddr++; } } } } uint64_t endPad = endAddr - curAddr; if (endPad) { printf("pad%d:\n", pad_count++); printf(".fill %lld,1,0xcc\n", endPad); } printf(".globl __jitEnd\n"); printf(".globl _jitEnd\n"); printf("__jitEnd:\n"); printf("_jitEnd:\n"); return 0; }