aboutsummaryrefslogtreecommitdiffstats
path: root/emulator/qtools
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2008-10-21 07:00:00 -0700
committerThe Android Open Source Project <initial-contribution@android.com>2008-10-21 07:00:00 -0700
commit1506a206c0a5e3b593c4c61a62b8805b64e98daf (patch)
treee20fe3eb0f693e87649fff1ce75e3f23330f69f8 /emulator/qtools
downloadsdk-1506a206c0a5e3b593c4c61a62b8805b64e98daf.zip
sdk-1506a206c0a5e3b593c4c61a62b8805b64e98daf.tar.gz
sdk-1506a206c0a5e3b593c4c61a62b8805b64e98daf.tar.bz2
Initial Contribution
Diffstat (limited to 'emulator/qtools')
-rw-r--r--emulator/qtools/Android.mk141
-rw-r--r--emulator/qtools/armdis.cpp905
-rw-r--r--emulator/qtools/armdis.h45
-rw-r--r--emulator/qtools/bb2sym.cpp140
-rw-r--r--emulator/qtools/bb_dump.cpp47
-rw-r--r--emulator/qtools/bbprof.cpp222
-rw-r--r--emulator/qtools/bitvector.h40
-rw-r--r--emulator/qtools/callstack.h760
-rw-r--r--emulator/qtools/check_trace.cpp61
-rw-r--r--emulator/qtools/coverage.cpp153
-rw-r--r--emulator/qtools/decoder.cpp278
-rw-r--r--emulator/qtools/decoder.h28
-rw-r--r--emulator/qtools/dmtrace.cpp253
-rw-r--r--emulator/qtools/dmtrace.h61
-rw-r--r--emulator/qtools/exc_dump.cpp28
-rw-r--r--emulator/qtools/gtrace.cpp152
-rw-r--r--emulator/qtools/gtrace.h69
-rw-r--r--emulator/qtools/hash_table.h193
-rw-r--r--emulator/qtools/hist_trace.cpp64
-rw-r--r--emulator/qtools/opcode.cpp204
-rw-r--r--emulator/qtools/opcode.h166
-rw-r--r--emulator/qtools/parse_options-inl.h147
-rw-r--r--emulator/qtools/parse_options.cpp119
-rw-r--r--emulator/qtools/parse_options.h32
-rw-r--r--emulator/qtools/post_trace.cpp151
-rw-r--r--emulator/qtools/profile_pid.cpp93
-rw-r--r--emulator/qtools/profile_trace.cpp131
-rw-r--r--emulator/qtools/q2dm.cpp275
-rw-r--r--emulator/qtools/q2g.cpp108
-rw-r--r--emulator/qtools/read_addr.cpp29
-rw-r--r--emulator/qtools/read_elf.cpp210
-rw-r--r--emulator/qtools/read_elf.h20
-rw-r--r--emulator/qtools/read_method.cpp52
-rw-r--r--emulator/qtools/read_pid.cpp71
-rw-r--r--emulator/qtools/read_trace.cpp165
-rw-r--r--emulator/qtools/stack_dump.cpp105
-rw-r--r--emulator/qtools/tests/common_head.mk25
-rw-r--r--emulator/qtools/tests/common_tail.mk3
-rw-r--r--emulator/qtools/tests/gtrace/Makefile18
-rw-r--r--emulator/qtools/tests/gtrace/test.c201
-rw-r--r--emulator/qtools/tests/macros.h93
-rw-r--r--emulator/qtools/tests/tests.ld10
-rw-r--r--emulator/qtools/thumbdis.cpp503
-rw-r--r--emulator/qtools/trace_reader.cpp1201
-rw-r--r--emulator/qtools/trace_reader.h1408
-rw-r--r--emulator/qtools/trace_reader_base.h332
46 files changed, 9512 insertions, 0 deletions
diff --git a/emulator/qtools/Android.mk b/emulator/qtools/Android.mk
new file mode 100644
index 0000000..afbb3e8
--- /dev/null
+++ b/emulator/qtools/Android.mk
@@ -0,0 +1,141 @@
+#
+# Copyright 2006 The Android Open Source Project
+#
+# Java method trace dump tool
+#
+
+LOCAL_PATH:= $(call my-dir)
+
+common_includes := external/qemu
+common_cflags := -O0 -g
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := post_trace.cpp trace_reader.cpp decoder.cpp
+LOCAL_C_INCLUDES += $(common_includes)
+LOCAL_CFLAGS += $(common_cflags)
+LOCAL_MODULE := post_trace
+include $(BUILD_HOST_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := read_trace.cpp trace_reader.cpp decoder.cpp armdis.cpp \
+ thumbdis.cpp opcode.cpp read_elf.cpp parse_options.cpp
+LOCAL_C_INCLUDES += $(common_includes)
+LOCAL_CFLAGS += $(common_cflags)
+LOCAL_MODULE := read_trace
+include $(BUILD_HOST_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := check_trace.cpp trace_reader.cpp decoder.cpp \
+ opcode.cpp read_elf.cpp parse_options.cpp
+LOCAL_C_INCLUDES += $(common_includes)
+LOCAL_CFLAGS += $(common_cflags)
+LOCAL_MODULE := check_trace
+include $(BUILD_HOST_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := bb_dump.cpp trace_reader.cpp decoder.cpp \
+ read_elf.cpp parse_options.cpp
+LOCAL_C_INCLUDES += $(common_includes)
+LOCAL_CFLAGS += $(common_cflags)
+LOCAL_MODULE := bb_dump
+include $(BUILD_HOST_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := bb2sym.cpp trace_reader.cpp decoder.cpp \
+ read_elf.cpp parse_options.cpp
+LOCAL_C_INCLUDES += $(common_includes)
+LOCAL_CFLAGS += $(common_cflags)
+LOCAL_MODULE := bb2sym
+include $(BUILD_HOST_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := profile_trace.cpp trace_reader.cpp decoder.cpp \
+ opcode.cpp read_elf.cpp parse_options.cpp
+LOCAL_C_INCLUDES += $(common_includes)
+LOCAL_CFLAGS += $(common_cflags)
+LOCAL_MODULE := profile_trace
+include $(BUILD_HOST_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := bbprof.cpp trace_reader.cpp decoder.cpp armdis.cpp \
+ thumbdis.cpp opcode.cpp
+LOCAL_C_INCLUDES += $(common_includes)
+LOCAL_CFLAGS += $(common_cflags)
+LOCAL_MODULE := bbprof
+include $(BUILD_HOST_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := q2g.cpp trace_reader.cpp decoder.cpp \
+ opcode.cpp read_elf.cpp parse_options.cpp gtrace.cpp
+LOCAL_C_INCLUDES += $(common_includes)
+LOCAL_CFLAGS += $(common_cflags)
+LOCAL_MODULE := q2g
+include $(BUILD_HOST_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := q2dm.cpp trace_reader.cpp decoder.cpp armdis.cpp \
+ thumbdis.cpp opcode.cpp read_elf.cpp parse_options.cpp dmtrace.cpp
+LOCAL_C_INCLUDES += $(common_includes)
+LOCAL_CFLAGS += $(common_cflags)
+LOCAL_MODULE := q2dm
+include $(BUILD_HOST_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := coverage.cpp trace_reader.cpp decoder.cpp armdis.cpp \
+ thumbdis.cpp opcode.cpp read_elf.cpp parse_options.cpp
+LOCAL_C_INCLUDES += $(common_includes)
+LOCAL_CFLAGS += $(common_cflags)
+LOCAL_MODULE := coverage
+include $(BUILD_HOST_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := stack_dump.cpp trace_reader.cpp decoder.cpp armdis.cpp \
+ thumbdis.cpp opcode.cpp read_elf.cpp parse_options.cpp
+LOCAL_C_INCLUDES += $(common_includes)
+LOCAL_CFLAGS += $(common_cflags)
+LOCAL_MODULE := stack_dump
+include $(BUILD_HOST_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := hist_trace.cpp trace_reader.cpp decoder.cpp
+LOCAL_C_INCLUDES += $(common_includes)
+LOCAL_CFLAGS += $(common_cflags)
+LOCAL_MODULE := hist_trace
+include $(BUILD_HOST_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := read_addr.cpp trace_reader.cpp decoder.cpp
+LOCAL_C_INCLUDES += $(common_includes)
+LOCAL_CFLAGS += $(common_cflags)
+LOCAL_MODULE := read_addr
+include $(BUILD_HOST_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := read_pid.cpp trace_reader.cpp decoder.cpp
+LOCAL_C_INCLUDES += $(common_includes)
+LOCAL_CFLAGS += $(common_cflags)
+LOCAL_MODULE := read_pid
+include $(BUILD_HOST_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := exc_dump.cpp trace_reader.cpp decoder.cpp
+LOCAL_C_INCLUDES += $(common_includes)
+LOCAL_CFLAGS += $(common_cflags)
+LOCAL_MODULE := exc_dump
+include $(BUILD_HOST_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := read_method.cpp trace_reader.cpp decoder.cpp \
+ read_elf.cpp parse_options.cpp
+LOCAL_C_INCLUDES += $(common_includes)
+LOCAL_CFLAGS += $(common_cflags)
+LOCAL_MODULE := read_method
+include $(BUILD_HOST_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := profile_pid.cpp trace_reader.cpp decoder.cpp \
+ read_elf.cpp parse_options.cpp
+LOCAL_C_INCLUDES += $(common_includes)
+LOCAL_CFLAGS += $(common_cflags)
+LOCAL_MODULE := profile_pid
+include $(BUILD_HOST_EXECUTABLE)
diff --git a/emulator/qtools/armdis.cpp b/emulator/qtools/armdis.cpp
new file mode 100644
index 0000000..593f460
--- /dev/null
+++ b/emulator/qtools/armdis.cpp
@@ -0,0 +1,905 @@
+// Copyright 2006 The Android Open Source Project
+
+#include <stdio.h>
+#include <string.h>
+#include "armdis.h"
+#include "opcode.h"
+
+static char *cond_names[] = {
+ "eq",
+ "ne",
+ "cs",
+ "cc",
+ "mi",
+ "pl",
+ "vs",
+ "vc",
+ "hi",
+ "ls",
+ "ge",
+ "lt",
+ "gt",
+ "le",
+ "",
+ "RESERVED"
+};
+
+// Indexed by the shift type (bits 6-5)
+static const char *shift_names[] = {
+ "LSL",
+ "LSR",
+ "ASR",
+ "ROR"
+};
+
+static char* cond_to_str(int cond) {
+ return cond_names[cond];
+}
+
+char *Arm::disasm(uint32_t addr, uint32_t insn, char *result)
+{
+ static char buf[80];
+ char *ptr;
+
+ ptr = result ? result : buf;
+ Opcode opcode = decode(insn);
+ switch (opcode) {
+ case OP_INVALID:
+ sprintf(ptr, "Invalid");
+ return ptr;
+ case OP_UNDEFINED:
+ sprintf(ptr, "Undefined");
+ return ptr;
+ case OP_ADC:
+ case OP_ADD:
+ case OP_AND:
+ case OP_BIC:
+ case OP_CMN:
+ case OP_CMP:
+ case OP_EOR:
+ case OP_MOV:
+ case OP_MVN:
+ case OP_ORR:
+ case OP_RSB:
+ case OP_RSC:
+ case OP_SBC:
+ case OP_SUB:
+ case OP_TEQ:
+ case OP_TST:
+ return disasm_alu(opcode, insn, ptr);
+ case OP_B:
+ case OP_BL:
+ return disasm_branch(addr, opcode, insn, ptr);
+ case OP_BKPT:
+ return disasm_bkpt(insn, ptr);
+ case OP_BLX:
+ // not supported yet
+ break;
+ case OP_BX:
+ return disasm_bx(insn, ptr);
+ case OP_CDP:
+ sprintf(ptr, "cdp");
+ return ptr;
+ case OP_CLZ:
+ return disasm_clz(insn, ptr);
+ case OP_LDC:
+ sprintf(ptr, "ldc");
+ return ptr;
+ case OP_LDM:
+ case OP_STM:
+ return disasm_memblock(opcode, insn, ptr);
+ case OP_LDR:
+ case OP_LDRB:
+ case OP_LDRBT:
+ case OP_LDRT:
+ case OP_STR:
+ case OP_STRB:
+ case OP_STRBT:
+ case OP_STRT:
+ return disasm_mem(insn, ptr);
+ case OP_LDRH:
+ case OP_LDRSB:
+ case OP_LDRSH:
+ case OP_STRH:
+ return disasm_memhalf(insn, ptr);
+ case OP_MCR:
+ case OP_MRC:
+ return disasm_mcr(opcode, insn, ptr);
+ case OP_MLA:
+ return disasm_mla(opcode, insn, ptr);
+ case OP_MRS:
+ return disasm_mrs(insn, ptr);
+ case OP_MSR:
+ return disasm_msr(insn, ptr);
+ case OP_MUL:
+ return disasm_mul(opcode, insn, ptr);
+ case OP_PLD:
+ return disasm_pld(insn, ptr);
+ case OP_STC:
+ sprintf(ptr, "stc");
+ return ptr;
+ case OP_SWI:
+ return disasm_swi(insn, ptr);
+ case OP_SWP:
+ case OP_SWPB:
+ return disasm_swp(opcode, insn, ptr);
+ case OP_UMLAL:
+ case OP_UMULL:
+ case OP_SMLAL:
+ case OP_SMULL:
+ return disasm_umlal(opcode, insn, ptr);
+ default:
+ sprintf(ptr, "Error");
+ return ptr;
+ }
+ return NULL;
+}
+
+char *Arm::disasm_alu(Opcode opcode, uint32_t insn, char *ptr)
+{
+ static const uint8_t kNoOperand1 = 1;
+ static const uint8_t kNoDest = 2;
+ static const uint8_t kNoSbit = 4;
+
+ char rn_str[20];
+ char rd_str[20];
+ uint8_t flags = 0;
+ uint8_t cond = (insn >> 28) & 0xf;
+ uint8_t is_immed = (insn >> 25) & 0x1;
+ uint8_t bit_s = (insn >> 20) & 1;
+ uint8_t rn = (insn >> 16) & 0xf;
+ uint8_t rd = (insn >> 12) & 0xf;
+ uint8_t immed = insn & 0xff;
+
+ const char *opname = opcode_names[opcode];
+ switch (opcode) {
+ case OP_CMN:
+ case OP_CMP:
+ case OP_TEQ:
+ case OP_TST:
+ flags = kNoDest | kNoSbit;
+ break;
+ case OP_MOV:
+ case OP_MVN:
+ flags = kNoOperand1;
+ break;
+ default:
+ break;
+ }
+
+ // The "mov" instruction ignores the first operand (rn).
+ rn_str[0] = 0;
+ if ((flags & kNoOperand1) == 0) {
+ sprintf(rn_str, "r%d, ", rn);
+ }
+
+ // The following instructions do not write the result register (rd):
+ // tst, teq, cmp, cmn.
+ rd_str[0] = 0;
+ if ((flags & kNoDest) == 0) {
+ sprintf(rd_str, "r%d, ", rd);
+ }
+
+ char *sbit_str = "";
+ if (bit_s && !(flags & kNoSbit))
+ sbit_str = "s";
+
+ if (is_immed) {
+ sprintf(ptr, "%s%s%s\t%s%s#%u ; 0x%x",
+ opname, cond_to_str(cond), sbit_str, rd_str, rn_str, immed, immed);
+ return ptr;
+ }
+
+ uint8_t shift_is_reg = (insn >> 4) & 1;
+ uint8_t rotate = (insn >> 8) & 0xf;
+ uint8_t rm = insn & 0xf;
+ uint8_t shift_type = (insn >> 5) & 0x3;
+ uint8_t rs = (insn >> 8) & 0xf;
+ uint8_t shift_amount = (insn >> 7) & 0x1f;
+ uint32_t rotated_val = immed;
+ uint8_t rotate2 = rotate << 1;
+ rotated_val = (rotated_val >> rotate2) | (rotated_val << (32 - rotate2));
+
+ if (!shift_is_reg && shift_type == 0 && shift_amount == 0) {
+ sprintf(ptr, "%s%s%s\t%s%sr%d",
+ opname, cond_to_str(cond), sbit_str, rd_str, rn_str, rm);
+ return ptr;
+ }
+
+ const char *shift_name = shift_names[shift_type];
+ if (shift_is_reg) {
+ sprintf(ptr, "%s%s%s\t%s%sr%d, %s r%d",
+ opname, cond_to_str(cond), sbit_str, rd_str, rn_str, rm,
+ shift_name, rs);
+ return ptr;
+ }
+ if (shift_amount == 0) {
+ if (shift_type == 3) {
+ sprintf(ptr, "%s%s%s\t%s%sr%d, RRX",
+ opname, cond_to_str(cond), sbit_str, rd_str, rn_str, rm);
+ return ptr;
+ }
+ shift_amount = 32;
+ }
+ sprintf(ptr, "%s%s%s\t%s%sr%d, %s #%u",
+ opname, cond_to_str(cond), sbit_str, rd_str, rn_str, rm,
+ shift_name, shift_amount);
+ return ptr;
+}
+
+char *Arm::disasm_branch(uint32_t addr, Opcode opcode, uint32_t insn, char *ptr)
+{
+ uint8_t cond = (insn >> 28) & 0xf;
+ uint32_t offset = insn & 0xffffff;
+ // Sign-extend the 24-bit offset
+ if ((offset >> 23) & 1)
+ offset |= 0xff000000;
+
+ // Pre-compute the left-shift and the prefetch offset
+ offset <<= 2;
+ offset += 8;
+ addr += offset;
+ const char *opname = opcode_names[opcode];
+ sprintf(ptr, "%s%s\t0x%x", opname, cond_to_str(cond), addr);
+ return ptr;
+}
+
+char *Arm::disasm_bx(uint32_t insn, char *ptr)
+{
+ uint8_t cond = (insn >> 28) & 0xf;
+ uint8_t rn = insn & 0xf;
+ sprintf(ptr, "bx%s\tr%d", cond_to_str(cond), rn);
+ return ptr;
+}
+
+char *Arm::disasm_bkpt(uint32_t insn, char *ptr)
+{
+ uint32_t immed = (((insn >> 8) & 0xfff) << 4) | (insn & 0xf);
+ sprintf(ptr, "bkpt\t#%d", immed);
+ return ptr;
+}
+
+char *Arm::disasm_clz(uint32_t insn, char *ptr)
+{
+ uint8_t cond = (insn >> 28) & 0xf;
+ uint8_t rd = (insn >> 12) & 0xf;
+ uint8_t rm = insn & 0xf;
+ sprintf(ptr, "clz%s\tr%d, r%d", cond_to_str(cond), rd, rm);
+ return ptr;
+}
+
+char *Arm::disasm_memblock(Opcode opcode, uint32_t insn, char *ptr)
+{
+ char tmp_reg[10], tmp_list[80];
+
+ uint8_t cond = (insn >> 28) & 0xf;
+ uint8_t write_back = (insn >> 21) & 0x1;
+ uint8_t bit_s = (insn >> 22) & 0x1;
+ uint8_t is_up = (insn >> 23) & 0x1;
+ uint8_t is_pre = (insn >> 24) & 0x1;
+ uint8_t rn = (insn >> 16) & 0xf;
+ uint16_t reg_list = insn & 0xffff;
+
+ const char *opname = opcode_names[opcode];
+
+ char *bang = "";
+ if (write_back)
+ bang = "!";
+
+ char *carret = "";
+ if (bit_s)
+ carret = "^";
+
+ char *comma = "";
+ tmp_list[0] = 0;
+ for (int ii = 0; ii < 16; ++ii) {
+ if (reg_list & (1 << ii)) {
+ sprintf(tmp_reg, "%sr%d", comma, ii);
+ strcat(tmp_list, tmp_reg);
+ comma = ",";
+ }
+ }
+
+ char *addr_mode = "";
+ if (is_pre) {
+ if (is_up) {
+ addr_mode = "ib";
+ } else {
+ addr_mode = "db";
+ }
+ } else {
+ if (is_up) {
+ addr_mode = "ia";
+ } else {
+ addr_mode = "da";
+ }
+ }
+
+ sprintf(ptr, "%s%s%s\tr%d%s, {%s}%s",
+ opname, cond_to_str(cond), addr_mode, rn, bang, tmp_list, carret);
+ return ptr;
+}
+
+char *Arm::disasm_mem(uint32_t insn, char *ptr)
+{
+ uint8_t cond = (insn >> 28) & 0xf;
+ uint8_t is_reg = (insn >> 25) & 0x1;
+ uint8_t is_load = (insn >> 20) & 0x1;
+ uint8_t write_back = (insn >> 21) & 0x1;
+ uint8_t is_byte = (insn >> 22) & 0x1;
+ uint8_t is_up = (insn >> 23) & 0x1;
+ uint8_t is_pre = (insn >> 24) & 0x1;
+ uint8_t rn = (insn >> 16) & 0xf;
+ uint8_t rd = (insn >> 12) & 0xf;
+ uint16_t offset = insn & 0xfff;
+
+ char *opname = "ldr";
+ if (!is_load)
+ opname = "str";
+
+ char *bang = "";
+ if (write_back)
+ bang = "!";
+
+ char *minus = "";
+ if (is_up == 0)
+ minus = "-";
+
+ char *byte = "";
+ if (is_byte)
+ byte = "b";
+
+ if (is_reg == 0) {
+ if (is_pre) {
+ if (offset == 0) {
+ sprintf(ptr, "%s%s%s\tr%d, [r%d]",
+ opname, cond_to_str(cond), byte, rd, rn);
+ } else {
+ sprintf(ptr, "%s%s%s\tr%d, [r%d, #%s%u]%s",
+ opname, cond_to_str(cond), byte, rd, rn, minus, offset, bang);
+ }
+ } else {
+ char *transfer = "";
+ if (write_back)
+ transfer = "t";
+ sprintf(ptr, "%s%s%s%s\tr%d, [r%d], #%s%u",
+ opname, cond_to_str(cond), byte, transfer, rd, rn, minus, offset);
+ }
+ return ptr;
+ }
+
+ uint8_t rm = insn & 0xf;
+ uint8_t shift_type = (insn >> 5) & 0x3;
+ uint8_t shift_amount = (insn >> 7) & 0x1f;
+
+ const char *shift_name = shift_names[shift_type];
+
+ if (is_pre) {
+ if (shift_amount == 0) {
+ if (shift_type == 0) {
+ sprintf(ptr, "%s%s%s\tr%d, [r%d, %sr%d]%s",
+ opname, cond_to_str(cond), byte, rd, rn, minus, rm, bang);
+ return ptr;
+ }
+ if (shift_type == 3) {
+ sprintf(ptr, "%s%s%s\tr%d, [r%d, %sr%d, RRX]%s",
+ opname, cond_to_str(cond), byte, rd, rn, minus, rm, bang);
+ return ptr;
+ }
+ shift_amount = 32;
+ }
+ sprintf(ptr, "%s%s%s\tr%d, [r%d, %sr%d, %s #%u]%s",
+ opname, cond_to_str(cond), byte, rd, rn, minus, rm,
+ shift_name, shift_amount, bang);
+ return ptr;
+ }
+
+ char *transfer = "";
+ if (write_back)
+ transfer = "t";
+
+ if (shift_amount == 0) {
+ if (shift_type == 0) {
+ sprintf(ptr, "%s%s%s%s\tr%d, [r%d], %sr%d",
+ opname, cond_to_str(cond), byte, transfer, rd, rn, minus, rm);
+ return ptr;
+ }
+ if (shift_type == 3) {
+ sprintf(ptr, "%s%s%s%s\tr%d, [r%d], %sr%d, RRX",
+ opname, cond_to_str(cond), byte, transfer, rd, rn, minus, rm);
+ return ptr;
+ }
+ shift_amount = 32;
+ }
+
+ sprintf(ptr, "%s%s%s%s\tr%d, [r%d], %sr%d, %s #%u",
+ opname, cond_to_str(cond), byte, transfer, rd, rn, minus, rm,
+ shift_name, shift_amount);
+ return ptr;
+}
+
+char *Arm::disasm_memhalf(uint32_t insn, char *ptr)
+{
+ uint8_t cond = (insn >> 28) & 0xf;
+ uint8_t is_load = (insn >> 20) & 0x1;
+ uint8_t write_back = (insn >> 21) & 0x1;
+ uint8_t is_immed = (insn >> 22) & 0x1;
+ uint8_t is_up = (insn >> 23) & 0x1;
+ uint8_t is_pre = (insn >> 24) & 0x1;
+ uint8_t rn = (insn >> 16) & 0xf;
+ uint8_t rd = (insn >> 12) & 0xf;
+ uint8_t bits_65 = (insn >> 5) & 0x3;
+ uint8_t rm = insn & 0xf;
+ uint8_t offset = (((insn >> 8) & 0xf) << 4) | (insn & 0xf);
+
+ char *opname = "ldr";
+ if (is_load == 0)
+ opname = "str";
+
+ char *width = "";
+ if (bits_65 == 1)
+ width = "h";
+ else if (bits_65 == 2)
+ width = "sb";
+ else
+ width = "sh";
+
+ char *bang = "";
+ if (write_back)
+ bang = "!";
+ char *minus = "";
+ if (is_up == 0)
+ minus = "-";
+
+ if (is_immed) {
+ if (is_pre) {
+ if (offset == 0) {
+ sprintf(ptr, "%s%sh\tr%d, [r%d]", opname, cond_to_str(cond), rd, rn);
+ } else {
+ sprintf(ptr, "%s%sh\tr%d, [r%d, #%s%u]%s",
+ opname, cond_to_str(cond), rd, rn, minus, offset, bang);
+ }
+ } else {
+ sprintf(ptr, "%s%sh\tr%d, [r%d], #%s%u",
+ opname, cond_to_str(cond), rd, rn, minus, offset);
+ }
+ return ptr;
+ }
+
+ if (is_pre) {
+ sprintf(ptr, "%s%sh\tr%d, [r%d, %sr%d]%s",
+ opname, cond_to_str(cond), rd, rn, minus, rm, bang);
+ } else {
+ sprintf(ptr, "%s%sh\tr%d, [r%d], %sr%d",
+ opname, cond_to_str(cond), rd, rn, minus, rm);
+ }
+ return ptr;
+}
+
+char *Arm::disasm_mcr(Opcode opcode, uint32_t insn, char *ptr)
+{
+ uint8_t cond = (insn >> 28) & 0xf;
+ uint8_t crn = (insn >> 16) & 0xf;
+ uint8_t crd = (insn >> 12) & 0xf;
+ uint8_t cpnum = (insn >> 8) & 0xf;
+ uint8_t opcode2 = (insn >> 5) & 0x7;
+ uint8_t crm = insn & 0xf;
+
+ const char *opname = opcode_names[opcode];
+ sprintf(ptr, "%s%s\t%d, 0, r%d, cr%d, cr%d, {%d}",
+ opname, cond_to_str(cond), cpnum, crd, crn, crm, opcode2);
+ return ptr;
+}
+
+char *Arm::disasm_mla(Opcode opcode, uint32_t insn, char *ptr)
+{
+ uint8_t cond = (insn >> 28) & 0xf;
+ uint8_t rd = (insn >> 16) & 0xf;
+ uint8_t rn = (insn >> 12) & 0xf;
+ uint8_t rs = (insn >> 8) & 0xf;
+ uint8_t rm = insn & 0xf;
+ uint8_t bit_s = (insn >> 20) & 1;
+
+ const char *opname = opcode_names[opcode];
+ sprintf(ptr, "%s%s%s\tr%d, r%d, r%d, r%d",
+ opname, cond_to_str(cond), bit_s ? "s" : "", rd, rm, rs, rn);
+ return ptr;
+}
+
+char *Arm::disasm_umlal(Opcode opcode, uint32_t insn, char *ptr)
+{
+ uint8_t cond = (insn >> 28) & 0xf;
+ uint8_t rdhi = (insn >> 16) & 0xf;
+ uint8_t rdlo = (insn >> 12) & 0xf;
+ uint8_t rs = (insn >> 8) & 0xf;
+ uint8_t rm = insn & 0xf;
+ uint8_t bit_s = (insn >> 20) & 1;
+
+ const char *opname = opcode_names[opcode];
+ sprintf(ptr, "%s%s%s\tr%d, r%d, r%d, r%d",
+ opname, cond_to_str(cond), bit_s ? "s" : "", rdlo, rdhi, rm, rs);
+ return ptr;
+}
+
+char *Arm::disasm_mul(Opcode opcode, uint32_t insn, char *ptr)
+{
+ uint8_t cond = (insn >> 28) & 0xf;
+ uint8_t rd = (insn >> 16) & 0xf;
+ uint8_t rs = (insn >> 8) & 0xf;
+ uint8_t rm = insn & 0xf;
+ uint8_t bit_s = (insn >> 20) & 1;
+
+ const char *opname = opcode_names[opcode];
+ sprintf(ptr, "%s%s%s\tr%d, r%d, r%d",
+ opname, cond_to_str(cond), bit_s ? "s" : "", rd, rm, rs);
+ return ptr;
+}
+
+char *Arm::disasm_mrs(uint32_t insn, char *ptr)
+{
+ uint8_t cond = (insn >> 28) & 0xf;
+ uint8_t rd = (insn >> 12) & 0xf;
+ uint8_t ps = (insn >> 22) & 1;
+
+ sprintf(ptr, "mrs%s\tr%d, %s", cond_to_str(cond), rd, ps ? "spsr" : "cpsr");
+ return ptr;
+}
+
+char *Arm::disasm_msr(uint32_t insn, char *ptr)
+{
+ char flags[8];
+ int flag_index = 0;
+ uint8_t cond = (insn >> 28) & 0xf;
+ uint8_t is_immed = (insn >> 25) & 0x1;
+ uint8_t pd = (insn >> 22) & 1;
+ uint8_t mask = (insn >> 16) & 0xf;
+
+ if (mask & 1)
+ flags[flag_index++] = 'c';
+ if (mask & 2)
+ flags[flag_index++] = 'x';
+ if (mask & 4)
+ flags[flag_index++] = 's';
+ if (mask & 8)
+ flags[flag_index++] = 'f';
+ flags[flag_index] = 0;
+
+ if (is_immed) {
+ uint32_t immed = insn & 0xff;
+ uint8_t rotate = (insn >> 8) & 0xf;
+ uint8_t rotate2 = rotate << 1;
+ uint32_t rotated_val = (immed >> rotate2) | (immed << (32 - rotate2));
+ sprintf(ptr, "msr%s\t%s_%s, #0x%x",
+ cond_to_str(cond), pd ? "spsr" : "cpsr", flags, rotated_val);
+ return ptr;
+ }
+
+ uint8_t rm = insn & 0xf;
+
+ sprintf(ptr, "msr%s\t%s_%s, r%d",
+ cond_to_str(cond), pd ? "spsr" : "cpsr", flags, rm);
+ return ptr;
+}
+
+char *Arm::disasm_pld(uint32_t insn, char *ptr)
+{
+ uint8_t is_reg = (insn >> 25) & 0x1;
+ uint8_t is_up = (insn >> 23) & 0x1;
+ uint8_t rn = (insn >> 16) & 0xf;
+
+ char *minus = "";
+ if (is_up == 0)
+ minus = "-";
+
+ if (is_reg) {
+ uint8_t rm = insn & 0xf;
+ sprintf(ptr, "pld\t[r%d, %sr%d]", rn, minus, rm);
+ return ptr;
+ }
+
+ uint16_t offset = insn & 0xfff;
+ if (offset == 0) {
+ sprintf(ptr, "pld\t[r%d]", rn);
+ } else {
+ sprintf(ptr, "pld\t[r%d, #%s%u]", rn, minus, offset);
+ }
+ return ptr;
+}
+
+char *Arm::disasm_swi(uint32_t insn, char *ptr)
+{
+ uint8_t cond = (insn >> 28) & 0xf;
+ uint32_t sysnum = insn & 0x00ffffff;
+
+ sprintf(ptr, "swi%s 0x%x", cond_to_str(cond), sysnum);
+ return ptr;
+}
+
+char *Arm::disasm_swp(Opcode opcode, uint32_t insn, char *ptr)
+{
+ uint8_t cond = (insn >> 28) & 0xf;
+ uint8_t rn = (insn >> 16) & 0xf;
+ uint8_t rd = (insn >> 12) & 0xf;
+ uint8_t rm = insn & 0xf;
+
+ const char *opname = opcode_names[opcode];
+ sprintf(ptr, "%s%s\tr%d, r%d, [r%d]", opname, cond_to_str(cond), rd, rm, rn);
+ return ptr;
+}
+
+Opcode Arm::decode(uint32_t insn) {
+ uint32_t bits27_26 = (insn >> 26) & 0x3;
+ switch (bits27_26) {
+ case 0x0:
+ return decode00(insn);
+ case 0x1:
+ return decode01(insn);
+ case 0x2:
+ return decode10(insn);
+ case 0x3:
+ return decode11(insn);
+ }
+ return OP_INVALID;
+}
+
+Opcode Arm::decode00(uint32_t insn) {
+ uint8_t bit25 = (insn >> 25) & 0x1;
+ uint8_t bit4 = (insn >> 4) & 0x1;
+ if (bit25 == 0 && bit4 == 1) {
+ if ((insn & 0x0ffffff0) == 0x012fff10) {
+ // Bx instruction
+ return OP_BX;
+ }
+ if ((insn & 0x0ff000f0) == 0x01600010) {
+ // Clz instruction
+ return OP_CLZ;
+ }
+ if ((insn & 0xfff000f0) == 0xe1200070) {
+ // Bkpt instruction
+ return OP_BKPT;
+ }
+ uint32_t bits7_4 = (insn >> 4) & 0xf;
+ if (bits7_4 == 0x9) {
+ if ((insn & 0x0ff00ff0) == 0x01000090) {
+ // Swp instruction
+ uint8_t bit22 = (insn >> 22) & 0x1;
+ if (bit22)
+ return OP_SWPB;
+ return OP_SWP;
+ }
+ // One of the multiply instructions
+ return decode_mul(insn);
+ }
+
+ uint8_t bit7 = (insn >> 7) & 0x1;
+ if (bit7 == 1) {
+ // One of the load/store halfword/byte instructions
+ return decode_ldrh(insn);
+ }
+ }
+
+ // One of the data processing instructions
+ return decode_alu(insn);
+}
+
+Opcode Arm::decode01(uint32_t insn) {
+ uint8_t is_reg = (insn >> 25) & 0x1;
+ uint8_t bit4 = (insn >> 4) & 0x1;
+ if (is_reg == 1 && bit4 == 1)
+ return OP_UNDEFINED;
+ uint8_t is_load = (insn >> 20) & 0x1;
+ uint8_t is_byte = (insn >> 22) & 0x1;
+ if ((insn & 0xfd70f000) == 0xf550f000) {
+ // Pre-load
+ return OP_PLD;
+ }
+ if (is_load) {
+ if (is_byte) {
+ // Load byte
+ return OP_LDRB;
+ }
+ // Load word
+ return OP_LDR;
+ }
+ if (is_byte) {
+ // Store byte
+ return OP_STRB;
+ }
+ // Store word
+ return OP_STR;
+}
+
+Opcode Arm::decode10(uint32_t insn) {
+ uint8_t bit25 = (insn >> 25) & 0x1;
+ if (bit25 == 0) {
+ // LDM/STM
+ uint8_t is_load = (insn >> 20) & 0x1;
+ if (is_load)
+ return OP_LDM;
+ return OP_STM;
+ }
+ // Branch or Branch with link
+ uint8_t is_link = (insn >> 24) & 1;
+ uint32_t offset = insn & 0xffffff;
+
+ // Sign-extend the 24-bit offset
+ if ((offset >> 23) & 1)
+ offset |= 0xff000000;
+
+ // Pre-compute the left-shift and the prefetch offset
+ offset <<= 2;
+ offset += 8;
+ if (is_link == 0)
+ return OP_B;
+ return OP_BL;
+}
+
+Opcode Arm::decode11(uint32_t insn) {
+ uint8_t bit25 = (insn >> 25) & 0x1;
+ if (bit25 == 0) {
+ // LDC, SDC
+ uint8_t is_load = (insn >> 20) & 0x1;
+ if (is_load) {
+ // LDC
+ return OP_LDC;
+ }
+ // STC
+ return OP_STC;
+ }
+
+ uint8_t bit24 = (insn >> 24) & 0x1;
+ if (bit24 == 0x1) {
+ // SWI
+ return OP_SWI;
+ }
+
+ uint8_t bit4 = (insn >> 4) & 0x1;
+ uint8_t cpnum = (insn >> 8) & 0xf;
+
+ if (cpnum == 15) {
+ // Special case for coprocessor 15
+ uint8_t opcode = (insn >> 21) & 0x7;
+ if (bit4 == 0 || opcode != 0) {
+ // This is an unexpected bit pattern. Create an undefined
+ // instruction in case this is ever executed.
+ return OP_UNDEFINED;
+ }
+
+ // MRC, MCR
+ uint8_t is_mrc = (insn >> 20) & 0x1;
+ if (is_mrc)
+ return OP_MRC;
+ return OP_MCR;
+ }
+
+ if (bit4 == 0) {
+ // CDP
+ return OP_CDP;
+ }
+ // MRC, MCR
+ uint8_t is_mrc = (insn >> 20) & 0x1;
+ if (is_mrc)
+ return OP_MRC;
+ return OP_MCR;
+}
+
+Opcode Arm::decode_mul(uint32_t insn) {
+ uint8_t bit24 = (insn >> 24) & 0x1;
+ if (bit24 != 0) {
+ // This is an unexpected bit pattern. Create an undefined
+ // instruction in case this is ever executed.
+ return OP_UNDEFINED;
+ }
+ uint8_t bit23 = (insn >> 23) & 0x1;
+ uint8_t bit22_U = (insn >> 22) & 0x1;
+ uint8_t bit21_A = (insn >> 21) & 0x1;
+ if (bit23 == 0) {
+ // 32-bit multiply
+ if (bit22_U != 0) {
+ // This is an unexpected bit pattern. Create an undefined
+ // instruction in case this is ever executed.
+ return OP_UNDEFINED;
+ }
+ if (bit21_A == 0)
+ return OP_MUL;
+ return OP_MLA;
+ }
+ // 64-bit multiply
+ if (bit22_U == 0) {
+ // Unsigned multiply long
+ if (bit21_A == 0)
+ return OP_UMULL;
+ return OP_UMLAL;
+ }
+ // Signed multiply long
+ if (bit21_A == 0)
+ return OP_SMULL;
+ return OP_SMLAL;
+}
+
+Opcode Arm::decode_ldrh(uint32_t insn) {
+ uint8_t is_load = (insn >> 20) & 0x1;
+ uint8_t bits_65 = (insn >> 5) & 0x3;
+ if (is_load) {
+ if (bits_65 == 0x1) {
+ // Load unsigned halfword
+ return OP_LDRH;
+ } else if (bits_65 == 0x2) {
+ // Load signed byte
+ return OP_LDRSB;
+ }
+ // Signed halfword
+ if (bits_65 != 0x3) {
+ // This is an unexpected bit pattern. Create an undefined
+ // instruction in case this is ever executed.
+ return OP_UNDEFINED;
+ }
+ // Load signed halfword
+ return OP_LDRSH;
+ }
+ // Store halfword
+ if (bits_65 != 0x1) {
+ // This is an unexpected bit pattern. Create an undefined
+ // instruction in case this is ever executed.
+ return OP_UNDEFINED;
+ }
+ // Store halfword
+ return OP_STRH;
+}
+
+Opcode Arm::decode_alu(uint32_t insn) {
+ uint8_t is_immed = (insn >> 25) & 0x1;
+ uint8_t opcode = (insn >> 21) & 0xf;
+ uint8_t bit_s = (insn >> 20) & 1;
+ uint8_t shift_is_reg = (insn >> 4) & 1;
+ uint8_t bit7 = (insn >> 7) & 1;
+ if (!is_immed && shift_is_reg && (bit7 != 0)) {
+ // This is an unexpected bit pattern. Create an undefined
+ // instruction in case this is ever executed.
+ return OP_UNDEFINED;
+ }
+ switch (opcode) {
+ case 0x0:
+ return OP_AND;
+ case 0x1:
+ return OP_EOR;
+ case 0x2:
+ return OP_SUB;
+ case 0x3:
+ return OP_RSB;
+ case 0x4:
+ return OP_ADD;
+ case 0x5:
+ return OP_ADC;
+ case 0x6:
+ return OP_SBC;
+ case 0x7:
+ return OP_RSC;
+ case 0x8:
+ if (bit_s)
+ return OP_TST;
+ return OP_MRS;
+ case 0x9:
+ if (bit_s)
+ return OP_TEQ;
+ return OP_MSR;
+ case 0xa:
+ if (bit_s)
+ return OP_CMP;
+ return OP_MRS;
+ case 0xb:
+ if (bit_s)
+ return OP_CMN;
+ return OP_MSR;
+ case 0xc:
+ return OP_ORR;
+ case 0xd:
+ return OP_MOV;
+ case 0xe:
+ return OP_BIC;
+ case 0xf:
+ return OP_MVN;
+ }
+ // Unreachable
+ return OP_INVALID;
+}
diff --git a/emulator/qtools/armdis.h b/emulator/qtools/armdis.h
new file mode 100644
index 0000000..230f833
--- /dev/null
+++ b/emulator/qtools/armdis.h
@@ -0,0 +1,45 @@
+// Copyright 2006 The Android Open Source Project
+
+#ifndef ARMDIS_H
+#define ARMDIS_H
+
+#include <inttypes.h>
+#include "opcode.h"
+
+class Arm {
+ public:
+ static char *disasm(uint32_t addr, uint32_t insn, char *buffer);
+ static Opcode decode(uint32_t insn);
+
+ private:
+ static Opcode decode00(uint32_t insn);
+ static Opcode decode01(uint32_t insn);
+ static Opcode decode10(uint32_t insn);
+ static Opcode decode11(uint32_t insn);
+ static Opcode decode_mul(uint32_t insn);
+ static Opcode decode_ldrh(uint32_t insn);
+ static Opcode decode_alu(uint32_t insn);
+
+ static char *disasm_alu(Opcode opcode, uint32_t insn, char *ptr);
+ static char *disasm_branch(uint32_t addr, Opcode opcode, uint32_t insn, char *ptr);
+ static char *disasm_bx(uint32_t insn, char *ptr);
+ static char *disasm_bkpt(uint32_t insn, char *ptr);
+ static char *disasm_clz(uint32_t insn, char *ptr);
+ static char *disasm_memblock(Opcode opcode, uint32_t insn, char *ptr);
+ static char *disasm_mem(uint32_t insn, char *ptr);
+ static char *disasm_memhalf(uint32_t insn, char *ptr);
+ static char *disasm_mcr(Opcode opcode, uint32_t insn, char *ptr);
+ static char *disasm_mla(Opcode opcode, uint32_t insn, char *ptr);
+ static char *disasm_umlal(Opcode opcode, uint32_t insn, char *ptr);
+ static char *disasm_mul(Opcode opcode, uint32_t insn, char *ptr);
+ static char *disasm_mrs(uint32_t insn, char *ptr);
+ static char *disasm_msr(uint32_t insn, char *ptr);
+ static char *disasm_pld(uint32_t insn, char *ptr);
+ static char *disasm_swi(uint32_t insn, char *ptr);
+ static char *disasm_swp(Opcode opcode, uint32_t insn, char *ptr);
+};
+
+extern char *disasm_insn_thumb(uint32_t pc, uint32_t insn1, uint32_t insn2, char *result);
+extern Opcode decode_insn_thumb(uint32_t given);
+
+#endif /* ARMDIS_H */
diff --git a/emulator/qtools/bb2sym.cpp b/emulator/qtools/bb2sym.cpp
new file mode 100644
index 0000000..8a18b67
--- /dev/null
+++ b/emulator/qtools/bb2sym.cpp
@@ -0,0 +1,140 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <assert.h>
+#include "trace_reader.h"
+#include "parse_options.h"
+
+typedef TraceReader<> TraceReaderType;
+
+#include "parse_options-inl.h"
+
+struct MyStaticRec {
+ StaticRec bb;
+ symbol_type *sym;
+ MyStaticRec *inner; // pointer to an inner basic block
+ int is_thumb;
+};
+
+MyStaticRec **assign_inner_blocks(int num_blocks, MyStaticRec *blocks);
+
+void Usage(const char *program)
+{
+ fprintf(stderr, "Usage: %s [options] trace_file elf_file\n", program);
+ OptionsUsage();
+}
+
+// This function is called from quicksort to compare addresses of basic
+// blocks.
+int cmp_inc_addr(const void *a, const void *b) {
+ MyStaticRec *bb1, *bb2;
+
+ bb1 = *(MyStaticRec**)a;
+ bb2 = *(MyStaticRec**)b;
+ if (bb1->bb.bb_addr < bb2->bb.bb_addr)
+ return -1;
+ if (bb1->bb.bb_addr > bb2->bb.bb_addr)
+ return 1;
+ return bb1->bb.bb_num - bb2->bb.bb_num;
+}
+
+int main(int argc, char **argv) {
+ uint32_t insns[kMaxInsnPerBB];
+
+ // Parse the options
+ ParseOptions(argc, argv);
+ if (argc - optind != 2) {
+ Usage(argv[0]);
+ exit(1);
+ }
+
+ char *trace_filename = argv[optind++];
+ char *elf_file = argv[optind++];
+ TraceReader<> *trace = new TraceReader<>;
+ trace->Open(trace_filename);
+ trace->ReadKernelSymbols(elf_file);
+ trace->SetRoot(root);
+
+ TraceHeader *header = trace->GetHeader();
+ uint32_t num_static_bb = header->num_static_bb;
+
+ // Allocate space for all of the static blocks
+ MyStaticRec *blocks = new MyStaticRec[num_static_bb];
+
+ // Read in all the static blocks
+ for (uint32_t ii = 0; ii < num_static_bb; ++ii) {
+ trace->ReadStatic(&blocks[ii].bb);
+ blocks[ii].is_thumb = blocks[ii].bb.bb_addr & 1;
+ blocks[ii].bb.bb_addr &= ~1;
+ blocks[ii].sym = NULL;
+ blocks[ii].inner = NULL;
+ trace->ReadStaticInsns(blocks[ii].bb.num_insns, insns);
+ }
+
+ MyStaticRec **sorted = assign_inner_blocks(num_static_bb, blocks);
+
+ while (1) {
+ symbol_type *sym;
+ BBEvent event;
+ BBEvent ignored;
+
+ if (GetNextValidEvent(trace, &event, &ignored, &sym))
+ break;
+
+ uint64_t bb_num = event.bb_num;
+ blocks[bb_num].sym = sym;
+ }
+
+ printf("# bb num_insns bb_addr file symbol\n");
+ for (uint32_t ii = 0; ii < num_static_bb; ++ii) {
+ if (sorted[ii]->bb.bb_addr == 0 || sorted[ii]->bb.num_insns == 0
+ || sorted[ii]->sym == NULL)
+ continue;
+
+ printf("%8lld %3d 0x%08x %s %s\n",
+ sorted[ii]->bb.bb_num, sorted[ii]->bb.num_insns,
+ sorted[ii]->bb.bb_addr, sorted[ii]->sym->region->path,
+ sorted[ii]->sym->name);
+ }
+ return 0;
+}
+
+// Find the basic blocks that are subsets of other basic blocks.
+MyStaticRec **assign_inner_blocks(int num_blocks, MyStaticRec *blocks)
+{
+ int ii;
+ uint32_t addr_end, addr_diff;
+
+ // Create a list of pointers to the basic blocks that we can sort.
+ MyStaticRec **sorted = new MyStaticRec*[num_blocks];
+ for (ii = 0; ii < num_blocks; ++ii) {
+ sorted[ii] = &blocks[ii];
+ }
+
+ // Sort the basic blocks into increasing address order
+ qsort(sorted, num_blocks, sizeof(MyStaticRec*), cmp_inc_addr);
+
+ // Create pointers to inner blocks and break up the enclosing block
+ // so that there is no overlap.
+ for (ii = 0; ii < num_blocks - 1; ++ii) {
+ int num_bytes;
+ if (sorted[ii]->is_thumb)
+ num_bytes = sorted[ii]->bb.num_insns << 1;
+ else
+ num_bytes = sorted[ii]->bb.num_insns << 2;
+ addr_end = sorted[ii]->bb.bb_addr + num_bytes;
+ if (addr_end > sorted[ii + 1]->bb.bb_addr) {
+ sorted[ii]->inner = sorted[ii + 1];
+ addr_diff = sorted[ii + 1]->bb.bb_addr - sorted[ii]->bb.bb_addr;
+ uint32_t num_insns;
+ if (sorted[ii]->is_thumb)
+ num_insns = addr_diff >> 1;
+ else
+ num_insns = addr_diff >> 2;
+ sorted[ii]->bb.num_insns = num_insns;
+ }
+ }
+
+ return sorted;
+}
diff --git a/emulator/qtools/bb_dump.cpp b/emulator/qtools/bb_dump.cpp
new file mode 100644
index 0000000..de241fb
--- /dev/null
+++ b/emulator/qtools/bb_dump.cpp
@@ -0,0 +1,47 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <assert.h>
+#include "trace_reader.h"
+#include "parse_options.h"
+
+typedef TraceReader<> TraceReaderType;
+
+#include "parse_options-inl.h"
+
+void Usage(const char *program)
+{
+ fprintf(stderr, "Usage: %s [options] trace_file elf_file\n", program);
+ OptionsUsage();
+}
+
+int main(int argc, char **argv) {
+ // Parse the options
+ ParseOptions(argc, argv);
+ if (argc - optind != 2) {
+ Usage(argv[0]);
+ exit(1);
+ }
+
+ char *trace_filename = argv[optind++];
+ char *elf_file = argv[optind++];
+ TraceReader<> *trace = new TraceReader<>;
+ trace->Open(trace_filename);
+ trace->ReadKernelSymbols(elf_file);
+ trace->SetRoot(root);
+
+ printf("# time bb pid num_insns bb_addr\n");
+ while (1) {
+ symbol_type *sym;
+ BBEvent event;
+ BBEvent ignored;
+
+ if (GetNextValidEvent(trace, &event, &ignored, &sym))
+ break;
+ printf("%7lld %4lld %5d %3d 0x%08x %s\n",
+ event.time, event.bb_num, event.pid, event.num_insns,
+ event.bb_addr, sym->name);
+ }
+ return 0;
+}
diff --git a/emulator/qtools/bbprof.cpp b/emulator/qtools/bbprof.cpp
new file mode 100644
index 0000000..36d0941
--- /dev/null
+++ b/emulator/qtools/bbprof.cpp
@@ -0,0 +1,222 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <inttypes.h>
+#include "trace_reader.h"
+#include "armdis.h"
+
+struct MyStaticRec {
+ StaticRec bb;
+ uint32_t *insns;
+ uint32_t *cycles; // number of cycles for each insn
+ uint32_t elapsed; // number of cycles for basic block
+ int freq; // execution frequency
+ MyStaticRec *inner; // pointer to an inner basic block
+ int is_thumb;
+};
+
+MyStaticRec **assign_inner_blocks(int num_blocks, MyStaticRec *blocks);
+
+// This function is called from quicksort to compare addresses of basic
+// blocks.
+int cmp_inc_addr(const void *a, const void *b) {
+ MyStaticRec *bb1, *bb2;
+
+ bb1 = *(MyStaticRec**)a;
+ bb2 = *(MyStaticRec**)b;
+ if (bb1->bb.bb_addr < bb2->bb.bb_addr)
+ return -1;
+ if (bb1->bb.bb_addr > bb2->bb.bb_addr)
+ return 1;
+ return bb1->bb.bb_num - bb2->bb.bb_num;
+}
+
+// This function is called from quicksort to compare the elapsed time
+// of basic blocks.
+int cmp_dec_elapsed(const void *a, const void *b) {
+ MyStaticRec *bb1, *bb2;
+
+ bb1 = *(MyStaticRec**)a;
+ bb2 = *(MyStaticRec**)b;
+ if (bb1->elapsed < bb2->elapsed)
+ return 1;
+ if (bb1->elapsed > bb2->elapsed)
+ return -1;
+ return bb1->bb.bb_num - bb2->bb.bb_num;
+}
+
+// This function is called from quicksort to compare frequencies of
+// basic blocks.
+int cmp_dec_freq(const void *a, const void *b) {
+ MyStaticRec *bb1, *bb2;
+
+ bb1 = *(MyStaticRec**)a;
+ bb2 = *(MyStaticRec**)b;
+ if (bb1->freq < bb2->freq)
+ return 1;
+ if (bb1->freq > bb2->freq)
+ return -1;
+ return bb1->bb.bb_num - bb2->bb.bb_num;
+}
+
+int main(int argc, char **argv)
+{
+ if (argc != 2) {
+ fprintf(stderr, "Usage: %s trace_file\n", argv[0]);
+ exit(1);
+ }
+
+ char *trace_filename = argv[1];
+ TraceReaderBase *trace = new TraceReaderBase;
+ trace->Open(trace_filename);
+ TraceHeader *header = trace->GetHeader();
+ uint32_t num_static_bb = header->num_static_bb;
+
+ // Allocate space for all of the static blocks
+ MyStaticRec *blocks = new MyStaticRec[num_static_bb];
+
+ // Read in all the static blocks
+ for (uint32_t ii = 0; ii < num_static_bb; ++ii) {
+ trace->ReadStatic(&blocks[ii].bb);
+ blocks[ii].is_thumb = blocks[ii].bb.bb_addr & 1;
+ blocks[ii].bb.bb_addr &= ~1;
+ uint32_t num_insns = blocks[ii].bb.num_insns;
+ blocks[ii].insns = new uint32_t[num_insns];
+ blocks[ii].cycles = new uint32_t[num_insns];
+ memset(blocks[ii].cycles, 0, num_insns * sizeof(uint32_t));
+ trace->ReadStaticInsns(num_insns, blocks[ii].insns);
+ blocks[ii].elapsed = 0;
+ blocks[ii].freq = 0;
+ blocks[ii].inner = NULL;
+ }
+
+ MyStaticRec **sorted = assign_inner_blocks(num_static_bb, blocks);
+
+ uint32_t prev_time = 0;
+ uint32_t elapsed = 0;
+ uint32_t dummy;
+ uint32_t *cycle_ptr = &dummy;
+ uint32_t *bb_elapsed_ptr = &dummy;
+ while (1) {
+ BBEvent event;
+
+ if (trace->ReadBB(&event))
+ break;
+ // Assign frequencies to each basic block
+ uint64_t bb_num = event.bb_num;
+ int num_insns = event.num_insns;
+ blocks[bb_num].freq += 1;
+ for (MyStaticRec *bptr = blocks[bb_num].inner; bptr; bptr = bptr->inner)
+ bptr->freq += 1;
+
+ // Assign simulation time to each instruction
+ for (MyStaticRec *bptr = &blocks[bb_num]; bptr; bptr = bptr->inner) {
+ uint32_t bb_num_insns = bptr->bb.num_insns;
+ for (uint32_t ii = 0; num_insns && ii < bb_num_insns; ++ii, --num_insns) {
+ uint32_t sim_time = trace->ReadInsnTime(event.time);
+ elapsed = sim_time - prev_time;
+ prev_time = sim_time;
+
+ // Attribute the elapsed time to the previous instruction and
+ // basic block.
+ *cycle_ptr += elapsed;
+ *bb_elapsed_ptr += elapsed;
+ cycle_ptr = &bptr->cycles[ii];
+ bb_elapsed_ptr = &bptr->elapsed;
+ }
+ }
+ }
+ *cycle_ptr += 1;
+ *bb_elapsed_ptr += 1;
+
+ // Sort the basic blocks into decreasing elapsed time
+ qsort(sorted, num_static_bb, sizeof(MyStaticRec*), cmp_dec_elapsed);
+
+ char spaces[80];
+ memset(spaces, ' ', 79);
+ spaces[79] = 0;
+ for (uint32_t ii = 0; ii < num_static_bb; ++ii) {
+ printf("bb %lld addr: 0x%x, insns: %d freq: %u elapsed: %u\n",
+ sorted[ii]->bb.bb_num, sorted[ii]->bb.bb_addr,
+ sorted[ii]->bb.num_insns, sorted[ii]->freq,
+ sorted[ii]->elapsed);
+ int num_insns = sorted[ii]->bb.num_insns;
+ uint32_t addr = sorted[ii]->bb.bb_addr;
+ for (int jj = 0; jj < num_insns; ++jj) {
+ uint32_t elapsed = sorted[ii]->cycles[jj];
+ uint32_t insn = sorted[ii]->insns[jj];
+ if (insn_is_thumb(insn)) {
+ insn = insn_unwrap_thumb(insn);
+
+ // thumb_pair is true if this is the first of a pair of
+ // thumb instructions (BL or BLX).
+ bool thumb_pair = ((insn & 0xf800) == 0xf000);
+
+ // Get the next thumb instruction (if any) because we may need
+ // it for the case where insn is BL or BLX.
+ uint32_t insn2 = 0;
+ if (thumb_pair && (jj + 1 < num_insns)) {
+ insn2 = sorted[ii]->insns[jj + 1];
+ insn2 = insn_unwrap_thumb(insn2);
+ jj += 1;
+ }
+ char *disasm = disasm_insn_thumb(addr, insn, insn2, NULL);
+ if (thumb_pair) {
+ printf(" %4u %08x %04x %04x %s\n", elapsed, addr, insn,
+ insn2, disasm);
+ addr += 2;
+ } else {
+ printf(" %4u %08x %04x %s\n", elapsed, addr, insn,
+ disasm);
+ }
+ addr += 2;
+ } else {
+ char *disasm = Arm::disasm(addr, insn, NULL);
+ printf(" %4u %08x %08x %s\n", elapsed, addr, insn, disasm);
+ addr += 4;
+ }
+ }
+ }
+
+ delete[] sorted;
+ return 0;
+}
+
+// Find the basic blocks that are subsets of other basic blocks.
+MyStaticRec **assign_inner_blocks(int num_blocks, MyStaticRec *blocks)
+{
+ int ii;
+ uint32_t addr_end, addr_diff;
+
+ // Create a list of pointers to the basic blocks that we can sort.
+ MyStaticRec **sorted = new MyStaticRec*[num_blocks];
+ for (ii = 0; ii < num_blocks; ++ii) {
+ sorted[ii] = &blocks[ii];
+ }
+
+ // Sort the basic blocks into increasing address order
+ qsort(sorted, num_blocks, sizeof(MyStaticRec*), cmp_inc_addr);
+
+ // Create pointers to inner blocks and break up the enclosing block
+ // so that there is no overlap.
+ for (ii = 0; ii < num_blocks - 1; ++ii) {
+ int num_bytes;
+ if (sorted[ii]->is_thumb)
+ num_bytes = sorted[ii]->bb.num_insns << 1;
+ else
+ num_bytes = sorted[ii]->bb.num_insns << 2;
+ addr_end = sorted[ii]->bb.bb_addr + num_bytes;
+ if (addr_end > sorted[ii + 1]->bb.bb_addr) {
+ sorted[ii]->inner = sorted[ii + 1];
+ addr_diff = sorted[ii + 1]->bb.bb_addr - sorted[ii]->bb.bb_addr;
+ uint32_t num_insns;
+ if (sorted[ii]->is_thumb)
+ num_insns = addr_diff >> 1;
+ else
+ num_insns = addr_diff >> 2;
+ sorted[ii]->bb.num_insns = num_insns;
+ }
+ }
+
+ return sorted;
+}
diff --git a/emulator/qtools/bitvector.h b/emulator/qtools/bitvector.h
new file mode 100644
index 0000000..d3f5cf1
--- /dev/null
+++ b/emulator/qtools/bitvector.h
@@ -0,0 +1,40 @@
+// Copyright 2006 The Android Open Source Project
+
+#ifndef BITVECTOR_H
+#define BITVECTOR_H
+
+#include <inttypes.h>
+#include <assert.h>
+
+class Bitvector {
+ public:
+ explicit Bitvector(int num_bits) {
+ num_bits_ = num_bits;
+
+ // Round up to a multiple of 32
+ num_bits = (num_bits + 31) & ~31;
+ vector_ = new uint32_t[num_bits >> 5];
+ }
+ ~Bitvector() {
+ delete[] vector_;
+ }
+
+ void SetBit(int bitnum) {
+ assert(bitnum < num_bits_);
+ vector_[bitnum >> 5] |= 1 << (bitnum & 31);
+ }
+ void ClearBit(int bitnum) {
+ assert(bitnum < num_bits_);
+ vector_[bitnum >> 5] &= ~(1 << (bitnum & 31));
+ }
+ bool GetBit(int bitnum) {
+ assert(bitnum < num_bits_);
+ return (vector_[bitnum >> 5] >> (bitnum & 31)) & 1;
+ }
+
+ private:
+ int num_bits_;
+ uint32_t *vector_;
+};
+
+#endif // BITVECTOR_H
diff --git a/emulator/qtools/callstack.h b/emulator/qtools/callstack.h
new file mode 100644
index 0000000..b869956
--- /dev/null
+++ b/emulator/qtools/callstack.h
@@ -0,0 +1,760 @@
+// Copyright 2006 The Android Open Source Project
+
+#ifndef CALL_STACK_H
+#define CALL_STACK_H
+
+#include "opcode.h"
+#include "armdis.h"
+
+class CallStackBase {
+ public:
+ int getId() { return mId; }
+ void setId(int id) { mId = id; }
+
+ private:
+ int mId;
+};
+
+// Define a template class for the stack frame. The template parameter
+// SYM is the symbol_type from the TraceReader<> template class. To
+// use the CallStack class, the user derives a subclass of StackFrame
+// and defines push() and pop() methods. This derived class is then
+// passed as a template parameter to CallStack.
+template <class SYM>
+class StackFrame {
+ public:
+
+ virtual ~StackFrame() {};
+
+ virtual void push(int stackLevel, uint64_t time, CallStackBase *base) {};
+ virtual void pop(int stackLevel, uint64_t time, CallStackBase *base) {};
+
+ typedef SYM symbol_type;
+ static const uint32_t kCausedException = 0x01;
+ static const uint32_t kInterpreted = 0x02;
+ static const uint32_t kPopBarrier = (kCausedException | kInterpreted);
+
+ symbol_type *function; // the symbol for the function we entered
+ uint32_t addr; // return address when this function returns
+ uint32_t flags;
+ uint32_t time; // for debugging when a problem occurred
+ uint32_t global_time; // for debugging when a problem occurred
+};
+
+template <class FRAME, class BASE = CallStackBase>
+class CallStack : public BASE {
+ public:
+ typedef typename FRAME::symbol_type symbol_type;
+ typedef typename FRAME::symbol_type::region_type region_type;
+ typedef BASE base_type;
+
+ CallStack(int id, int numFrames, TraceReaderType *trace);
+ ~CallStack();
+
+ void updateStack(BBEvent *event, symbol_type *function);
+ void popAll(uint64_t time);
+ void threadStart(uint64_t time);
+ void threadStop(uint64_t time);
+
+ // Set to true if you don't want to see any Java methods
+ void setNativeOnly(bool nativeOnly) {
+ mNativeOnly = nativeOnly;
+ }
+
+ int getStackLevel() { return mTop; }
+
+ uint64_t getGlobalTime(uint64_t time) { return time + mSkippedTime; }
+ void showStack();
+ void showSnapshotStack();
+
+ private:
+ enum Action { NONE, PUSH, POP };
+
+ Action getAction(BBEvent *event, symbol_type *function);
+ Action getMethodAction(BBEvent *event, symbol_type *function);
+ void doSimplePush(symbol_type *function, uint32_t addr,
+ uint64_t time);
+ void doSimplePop(uint64_t time);
+ void doPush(BBEvent *event, symbol_type *function);
+ void doPop(BBEvent *event, symbol_type *function, Action methodAction);
+
+ void transitionToJava();
+ void transitionFromJava(uint64_t time);
+
+ TraceReaderType *mTrace;
+ bool mNativeOnly;
+
+ symbol_type mDummyFunction;
+ region_type mDummyRegion;
+
+ int mNumFrames;
+ FRAME *mFrames;
+ int mTop; // index of the next stack frame to write
+
+ int mJavaTop;
+
+ int mSnapshotNumFrames;
+ FRAME *mSnapshotFrames;
+ int mSnapshotTop; // index of the next stack frame to write
+
+ symbol_type *mPrevFunction;
+ BBEvent mPrevEvent;
+
+ symbol_type *mUserFunction;
+ BBEvent mUserEvent; // the previous user-mode event
+
+ uint64_t mSkippedTime;
+ uint64_t mLastRunTime;
+
+ static MethodRec sCurrentMethod;
+ static MethodRec sNextMethod;
+};
+
+template<class FRAME, class BASE>
+MethodRec CallStack<FRAME, BASE>::sCurrentMethod;
+template<class FRAME, class BASE>
+MethodRec CallStack<FRAME, BASE>::sNextMethod;
+
+template<class FRAME, class BASE>
+CallStack<FRAME, BASE>::CallStack(int id, int numFrames, TraceReaderType *trace)
+{
+ mNativeOnly = false;
+ mTrace = trace;
+ BASE::setId(id);
+ mNumFrames = numFrames;
+ mFrames = new FRAME[mNumFrames];
+ mTop = 0;
+
+ mSnapshotNumFrames = numFrames;
+ mSnapshotFrames = new FRAME[mSnapshotNumFrames];
+ mSnapshotTop = 0;
+
+ memset(&mDummyFunction, 0, sizeof(symbol_type));
+ memset(&mDummyRegion, 0, sizeof(region_type));
+ mDummyFunction.region = &mDummyRegion;
+ mPrevFunction = &mDummyFunction;
+ memset(&mPrevEvent, 0, sizeof(BBEvent));
+ mUserFunction = &mDummyFunction;
+ memset(&mUserEvent, 0, sizeof(BBEvent));
+ mSkippedTime = 0;
+ mLastRunTime = 0;
+ mJavaTop = 0;
+
+ // Read the first two methods from the trace if we haven't already read
+ // from the method trace yet.
+ if (sCurrentMethod.time == 0) {
+ if (mTrace->ReadMethod(&sCurrentMethod)) {
+ sCurrentMethod.time = ~0ull;
+ sNextMethod.time = ~0ull;
+ }
+ if (sNextMethod.time != ~0ull && mTrace->ReadMethod(&sNextMethod)) {
+ sNextMethod.time = ~0ull;
+ }
+ }
+}
+
+template<class FRAME, class BASE>
+CallStack<FRAME, BASE>::~CallStack()
+{
+ delete mFrames;
+}
+
+template<class FRAME, class BASE>
+void
+CallStack<FRAME, BASE>::updateStack(BBEvent *event, symbol_type *function)
+{
+ if (mNativeOnly) {
+ // If this is an interpreted function, then use the native VM function
+ // instead.
+ if (function->vm_sym != NULL)
+ function = function->vm_sym;
+ }
+
+ Action action = getAction(event, function);
+ Action methodAction = getMethodAction(event, function);
+
+ // Pop off native functions before pushing or popping Java methods.
+ if (action == POP && mPrevFunction->vm_sym == NULL) {
+ // Pop off the previous function first.
+ doPop(event, function, NONE);
+ if (methodAction == POP) {
+ doPop(event, function, POP);
+ } else if (methodAction == PUSH) {
+ doPush(event, function);
+ }
+ } else {
+ if (methodAction != NONE) {
+ // If the method trace has a push or pop, then do it.
+ action = methodAction;
+ } else if (function->vm_sym != NULL) {
+ // This function is a Java method. Don't push or pop the
+ // Java method without a corresponding method trace record.
+ action = NONE;
+ }
+ if (action == POP) {
+ doPop(event, function, methodAction);
+ } else if (action == PUSH) {
+ doPush(event, function);
+ }
+ }
+
+ // If the stack is now empty, then push the current function.
+ if (mTop == 0) {
+ uint64_t time = event->time - mSkippedTime;
+ doSimplePush(function, 0, time);
+ }
+
+ mPrevFunction = function;
+ mPrevEvent = *event;
+}
+
+template<class FRAME, class BASE>
+void
+CallStack<FRAME, BASE>::threadStart(uint64_t time)
+{
+ mSkippedTime += time - mLastRunTime;
+}
+
+template<class FRAME, class BASE>
+void
+CallStack<FRAME, BASE>::threadStop(uint64_t time)
+{
+ mLastRunTime = time;
+}
+
+template<class FRAME, class BASE>
+typename CallStack<FRAME, BASE>::Action
+CallStack<FRAME, BASE>::getAction(BBEvent *event, symbol_type *function)
+{
+ Action action;
+ uint32_t offset;
+
+ // Compute the offset from the start of the function to this basic
+ // block address.
+ offset = event->bb_addr - function->addr - function->region->base_addr;
+
+ // Get the previously executed instruction
+ Opcode op = OP_INVALID;
+ int numInsns = mPrevEvent.num_insns;
+ uint32_t insn = 0;
+ if (numInsns > 0) {
+ insn = mPrevEvent.insns[numInsns - 1];
+ if (mPrevEvent.is_thumb) {
+ insn = insn_unwrap_thumb(insn);
+ op = decode_insn_thumb(insn);
+ } else {
+ op = Arm::decode(insn);
+ }
+ }
+
+ // The number of bytes in the previous basic block depends on
+ // whether the basic block was ARM or THUMB instructions.
+ int numBytes;
+ if (mPrevEvent.is_thumb) {
+ numBytes = numInsns << 1;
+ } else {
+ numBytes = numInsns << 2;
+ }
+
+ // If this basic block follows the previous one, then return NONE.
+ // If we don't do this, then we may be fooled into thinking this
+ // is a POP if the previous block ended with a conditional
+ // (non-executed) ldmia instruction. We do this check before
+ // checking if we are in a different function because we otherwise
+ // we might be fooled into thinking this is a PUSH to a new function
+ // when it is really just a fall-thru into a local kernel symbol
+ // that just looks like a new function.
+ uint32_t prev_end_addr = mPrevEvent.bb_addr + numBytes;
+ if (prev_end_addr == event->bb_addr) {
+ return NONE;
+ }
+
+ // If this basic block is in the same function as the last basic block,
+ // then just return NONE (but see the exceptions below).
+ // Exception 1: if the function calls itself (offset == 0) then we
+ // want to push this function.
+ // Exception 2: if the function returns to itself, then we want
+ // to pop this function. We detect this case by checking if the last
+ // instruction in the previous basic block was a load-multiple (ldm)
+ // and included r15 as one of the loaded registers.
+ if (function == mPrevFunction) {
+ if (numInsns > 0) {
+ // If this is the beginning of the function and the previous
+ // instruction was not a branch, then it's a PUSH.
+ if (offset == 0 && op != OP_B && op != OP_THUMB_B)
+ return PUSH;
+
+ // If the previous instruction was an ldm that loaded r15,
+ // then it's a POP.
+ if (offset != 0 && ((op == OP_LDM && (insn & 0x8000))
+ || (op == OP_THUMB_POP && (insn & 0x100)))) {
+ return POP;
+ }
+ }
+
+ return NONE;
+ }
+
+ // We have to figure out if this new function is a call or a
+ // return. We don't necessarily have a complete call stack (since
+ // we could have started tracing at any point), so we have to use
+ // heuristics. If the address we are jumping to is the beginning
+ // of a function, or if the instruction that took us there was
+ // either "bl" or "blx" then this is a PUSH. Also, if the
+ // function offset is non-zero and the previous instruction is a
+ // branch instruction, we will call it a PUSH. This happens in
+ // the kernel a lot when there is a branch to an offset from a
+ // label. A couple more special cases:
+ //
+ // - entering a .plt section ("procedure linkage table") is a PUSH,
+ // - an exception that jumps into the kernel vector entry point
+ // is also a push.
+ //
+ // If the function offset is non-zero and the previous instruction
+ // is a bx or some non-branch instruction, then it's a POP.
+ //
+ // There's another special case that comes up. The user code
+ // might execute an instruction that returns but before the pc
+ // starts executing in the caller, a kernel interrupt occurs.
+ // But it may be hard to tell if this is a return until after
+ // the kernel interrupt code is done and returns to user space.
+ // So we save the last user basic block and look at it when
+ // we come back into user space.
+
+ const uint32_t kIsKernelRegion = region_type::kIsKernelRegion;
+
+ if (((mPrevFunction->region->flags & kIsKernelRegion) == 0)
+ && (function->region->flags & kIsKernelRegion)) {
+ // We just switched into the kernel. Save the previous
+ // user-mode basic block and function.
+ mUserEvent = mPrevEvent;
+ mUserFunction = mPrevFunction;
+ } else if ((mPrevFunction->region->flags & kIsKernelRegion)
+ && ((function->region->flags & kIsKernelRegion) == 0)) {
+ // We just switched from kernel to user mode.
+ return POP;
+ }
+
+ action = PUSH;
+ if (offset != 0 && mPrevFunction != &mDummyFunction) {
+ // We are jumping into the middle of a function, so this is
+ // probably a return, not a function call. But look at the
+ // previous instruction first to see if it was a branch-and-link.
+
+ // If the previous instruction was not a branch (and not a
+ // branch-and-link) then POP; or if it is a "bx" instruction
+ // then POP because that is used to return from functions.
+ if (!isBranch(op) || op == OP_BX || op == OP_THUMB_BX) {
+ action = POP;
+ } else if (isBranch(op) && !isBranchLink(op)) {
+ // If the previous instruction was a normal branch to a
+ // local symbol then don't count it as a push or a pop.
+ action = NONE;
+ }
+
+ if (function->flags & symbol_type::kIsVectorTable)
+ action = PUSH;
+ }
+ return action;
+}
+
+
+template<class FRAME, class BASE>
+void CallStack<FRAME, BASE>::doPush(BBEvent *event, symbol_type *function)
+{
+ uint64_t time = event->time - mSkippedTime;
+
+ // Check for stack overflow
+ if (mTop >= mNumFrames) {
+#if 0
+ showStack();
+#endif
+ fprintf(stderr, "Error: stack overflow (%d frames)\n", mTop);
+ exit(1);
+ }
+
+ // Compute the return address here because we may need to change
+ // it if we are popping off a frame for a vector table.
+ int numBytes;
+ if (mPrevEvent.is_thumb) {
+ numBytes = mPrevEvent.num_insns << 1;
+ } else {
+ numBytes = mPrevEvent.num_insns << 2;
+ }
+ uint32_t retAddr = mPrevEvent.bb_addr + numBytes;
+
+ // If this is a Java method then set the return address to zero.
+ // We won't be using it for popping the method and it may lead
+ // to false matches when searching the stack.
+ if (function->vm_sym != NULL) {
+ retAddr = 0;
+ }
+
+#if 0
+ if (function->flags & symbol_type::kIsVectorStart) {
+ printf("stack before entering exception\n");
+ showStack();
+ }
+#endif
+
+ // If the previous function was a vector table, then pop it
+ // off before pushing on the new function. Also, change the
+ // return address for the new function to the return address
+ // from the vector table.
+ if ((mPrevFunction->flags & symbol_type::kIsVectorTable) && mTop > 0) {
+ retAddr = mFrames[mTop - 1].addr;
+ doSimplePop(time);
+ }
+
+ const uint32_t kIsKernelRegion = region_type::kIsKernelRegion;
+
+ // The following code handles the case where one function, F1,
+ // calls another function, F2, but the before F2 can start
+ // executing, it takes a page fault (on the first instruction
+ // in F2). The kernel is entered, handles the page fault, and
+ // then returns to the called function. The problem is that
+ // this looks like a new function call to F2 from the kernel.
+ // The following code cleans up the stack by popping the
+ // kernel frames back to F1 (but not including F1). The
+ // return address for F2 also has to be fixed up to point to
+ // F1 instead of the kernel.
+ //
+ // We detect this case by checking if the previous basic block
+ // was in the kernel and the current basic block is not.
+ if ((mPrevFunction->region->flags & kIsKernelRegion)
+ && ((function->region->flags & kIsKernelRegion) == 0)
+ && mTop > 0) {
+ // We are switching from kernel mode to user mode.
+#if 0
+ printf(" doPush(): popping to user mode, bb_addr: 0x%08x\n",
+ event->bb_addr);
+ showStack();
+#endif
+ do {
+ // Pop off the kernel frames until we reach the one that
+ // caused the exception.
+ doSimplePop(time);
+
+ // If the next stack frame is the one that caused an
+ // exception then stop popping frames.
+ if (mTop > 0
+ && (mFrames[mTop - 1].flags & FRAME::kCausedException)) {
+ mFrames[mTop - 1].flags &= ~FRAME::kCausedException;
+ retAddr = mFrames[mTop].addr;
+ break;
+ }
+ } while (mTop > 0);
+#if 0
+ printf(" doPush() popping to level %d, using retAddr 0x%08x\n",
+ mTop, retAddr);
+#endif
+ }
+
+ // If we are starting an exception handler, then mark the previous
+ // stack frame so that we know where to return when the exception
+ // handler finishes.
+ if ((function->flags & symbol_type::kIsVectorStart) && mTop > 0)
+ mFrames[mTop - 1].flags |= FRAME::kCausedException;
+
+ doSimplePush(function, retAddr, time);
+}
+
+template<class FRAME, class BASE>
+void CallStack<FRAME, BASE>::doSimplePush(symbol_type *function,
+ uint32_t addr, uint64_t time)
+{
+ // Check for stack overflow
+ if (mTop >= mNumFrames) {
+ showStack();
+ fprintf(stderr, "too many stack frames (%d)\n", mTop);
+ exit(1);
+ }
+
+ // Keep track of the number of Java methods we push on the stack.
+ if (!mNativeOnly && function->vm_sym != NULL) {
+ // If we are pushing the first Java method on the stack, then
+ // save a snapshot of the stack so that we can clean things up
+ // later when we pop off the last Java stack frame.
+ if (mJavaTop == 0) {
+ transitionToJava();
+ }
+ mJavaTop += 1;
+ }
+
+ mFrames[mTop].addr = addr;
+ mFrames[mTop].function = function;
+ mFrames[mTop].flags = 0;
+ mFrames[mTop].time = time;
+ mFrames[mTop].global_time = time + mSkippedTime;
+
+ // If the function being pushed is a Java method, then mark it on
+ // the stack so that we don't pop it off until we get a matching
+ // trace record from the method trace file.
+ if (function->vm_sym != NULL) {
+ mFrames[mTop].flags = FRAME::kInterpreted;
+ }
+
+ mFrames[mTop].push(mTop, time, this);
+ mTop += 1;
+}
+
+template<class FRAME, class BASE>
+void CallStack<FRAME, BASE>::doSimplePop(uint64_t time)
+{
+ if (mTop <= 0) {
+ return;
+ }
+
+ mTop -= 1;
+ mFrames[mTop].pop(mTop, time, this);
+
+ // Keep track of the number of Java methods we have on the stack.
+ symbol_type *function = mFrames[mTop].function;
+ if (!mNativeOnly && function->vm_sym != NULL) {
+ mJavaTop -= 1;
+
+ // When there are no more Java stack frames, then clean up
+ // the client's stack. We need to do this because the client
+ // doesn't see the changes to the native stack underlying the
+ // fake Java stack until the last Java method is popped off.
+ if (mJavaTop == 0) {
+ transitionFromJava(time);
+ }
+ }
+}
+
+template<class FRAME, class BASE>
+void CallStack<FRAME, BASE>::doPop(BBEvent *event, symbol_type *function,
+ Action methodAction)
+{
+ uint64_t time = event->time - mSkippedTime;
+
+ // Search backward on the stack for a matching return address.
+ // The most common case is that we pop one stack frame, but
+ // sometimes we pop more than one.
+ int stackLevel;
+ bool allowMethodPop = (methodAction == POP);
+ for (stackLevel = mTop - 1; stackLevel >= 0; --stackLevel) {
+ if (event->bb_addr == mFrames[stackLevel].addr) {
+ // We found a matching return address on the stack.
+ break;
+ }
+
+ // If this stack frame caused an exception, then do not pop
+ // this stack frame.
+ if (mFrames[stackLevel].flags & FRAME::kPopBarrier) {
+ // If this is a Java method, then allow a pop only if we
+ // have a matching trace record.
+ if (mFrames[stackLevel].flags & FRAME::kInterpreted) {
+ if (allowMethodPop) {
+ // Allow at most one method pop
+ allowMethodPop = false;
+ continue;
+ }
+ }
+ stackLevel += 1;
+ break;
+ }
+ }
+
+ // If we didn't find a matching return address then search the stack
+ // again for a matching function.
+ if (stackLevel < 0 || event->bb_addr != mFrames[stackLevel].addr) {
+ bool allowMethodPop = (methodAction == POP);
+ for (stackLevel = mTop - 1; stackLevel >= 0; --stackLevel) {
+ // Compare the function with the one in the stack frame.
+ if (function == mFrames[stackLevel].function) {
+ // We found a matching function. We want to pop up to but not
+ // including this frame.
+ stackLevel += 1;
+ break;
+ }
+
+ // If this stack frame caused an exception, then do not pop
+ // this stack frame.
+ if (mFrames[stackLevel].flags & FRAME::kPopBarrier) {
+ // If this is a Java method, then allow a pop only if we
+ // have a matching trace record.
+ if (mFrames[stackLevel].flags & FRAME::kInterpreted) {
+ if (allowMethodPop) {
+ // Allow at most one method pop
+ allowMethodPop = false;
+ continue;
+ }
+ }
+ stackLevel += 1;
+ break;
+ }
+ }
+ if (stackLevel < 0)
+ stackLevel = 0;
+ }
+
+ // Note that if we didn't find a matching stack frame, we will pop
+ // the whole stack (unless there is a Java method or exception
+ // frame on the stack). This is intentional because we may have
+ // started the trace in the middle of an executing program that is
+ // returning up the stack and we do not know the whole stack. So
+ // the right thing to do is to empty the stack.
+
+ // If we are emptying the stack, then add the current function
+ // on top. If the current function is the same as the top of
+ // stack, then avoid an extraneous pop and push.
+ if (stackLevel == 0 && mFrames[0].function == function)
+ stackLevel = 1;
+
+#if 0
+ if (mTop - stackLevel > 7) {
+ printf("popping thru level %d\n", stackLevel);
+ showStack();
+ }
+#endif
+
+ // Pop the stack frames
+ for (int ii = mTop - 1; ii >= stackLevel; --ii)
+ doSimplePop(time);
+
+ // Clear the "caused exception" bit on the current stack frame
+ if (mTop > 0) {
+ mFrames[mTop - 1].flags &= ~FRAME::kCausedException;
+ }
+
+ // Also handle the case where F1 calls F2 and F2 returns to
+ // F1, but before we can execute any instructions in F1, we
+ // switch to the kernel. Then when we return from the kernel
+ // we want to pop off F2 from the stack instead of pushing F1
+ // on top of F2. To handle this case, we saved the last
+ // user-mode basic block when we entered the kernel (in
+ // the getAction() function) and now we can check to see if
+ // that was a return to F1 instead of a call. We use the
+ // getAction() function to determine this.
+ const uint32_t kIsKernelRegion = region_type::kIsKernelRegion;
+ if ((mPrevFunction->region->flags & kIsKernelRegion)
+ && ((function->region->flags & kIsKernelRegion) == 0)) {
+ mPrevEvent = mUserEvent;
+ mPrevFunction = mUserFunction;
+ if (getAction(event, function) == POP) {
+ // We may need to pop more than one frame, so just
+ // call doPop() again. This won't be an infinite loop
+ // here because we changed mPrevEvent to the last
+ // user-mode event.
+ doPop(event, function, methodAction);
+ return;
+ }
+ }
+}
+
+template<class FRAME, class BASE>
+void CallStack<FRAME, BASE>::popAll(uint64_t time)
+{
+ time -= mSkippedTime;
+ while (mTop != 0) {
+ doSimplePop(time);
+ }
+}
+
+template<class FRAME, class BASE>
+typename CallStack<FRAME, BASE>::Action
+CallStack<FRAME, BASE>::getMethodAction(BBEvent *event, symbol_type *function)
+{
+ if (function->vm_sym == NULL && mPrevFunction->vm_sym == NULL) {
+ return NONE;
+ }
+
+ Action action = NONE;
+ uint32_t prevAddr = mPrevFunction->addr + mPrevFunction->region->base_addr;
+ uint32_t addr = function->addr + function->region->base_addr;
+
+ // If the events get ahead of the method trace, then read ahead until we
+ // sync up again. This can happen if there is a pop of a method in the
+ // method trace for which we don't have a previous push.
+ while (event->time >= sNextMethod.time) {
+ sCurrentMethod = sNextMethod;
+ if (mTrace->ReadMethod(&sNextMethod)) {
+ sNextMethod.time = ~0ull;
+ }
+ }
+
+ if (event->time >= sCurrentMethod.time) {
+ if (addr == sCurrentMethod.addr || prevAddr == sCurrentMethod.addr) {
+ action = (sCurrentMethod.flags == 0) ? PUSH : POP;
+ // We found a match, so read the next record.
+ sCurrentMethod = sNextMethod;
+ if (sNextMethod.time != ~0ull && mTrace->ReadMethod(&sNextMethod)) {
+ sNextMethod.time = ~0ull;
+ }
+ }
+ }
+ return action;
+}
+
+// When the first Java method is pushed on the stack, this method is
+// called to save a snapshot of the current native stack so that the
+// client's view of the native stack can be patched up later when the
+// Java stack is empty.
+template<class FRAME, class BASE>
+void CallStack<FRAME, BASE>::transitionToJava()
+{
+ mSnapshotTop = mTop;
+ for (int ii = 0; ii < mTop; ++ii) {
+ mSnapshotFrames[ii] = mFrames[ii];
+ }
+}
+
+// When the Java stack becomes empty, the native stack becomes
+// visible. This method is called when the Java stack becomes empty
+// to patch up the client's view of the native stack, which may have
+// changed underneath the Java stack. The stack snapshot is used to
+// create a sequence of pops and pushes to make the client's view of
+// the native stack match the current native stack.
+template<class FRAME, class BASE>
+void CallStack<FRAME, BASE>::transitionFromJava(uint64_t time)
+{
+ int top = mTop;
+ if (top > mSnapshotTop) {
+ top = mSnapshotTop;
+ }
+ for (int ii = 0; ii < top; ++ii) {
+ if (mSnapshotFrames[ii].function->addr == mFrames[ii].function->addr) {
+ continue;
+ }
+
+ // Pop off all the rest of the frames from the snapshot
+ for (int jj = top - 1; jj >= ii; --jj) {
+ mSnapshotFrames[jj].pop(jj, time, this);
+ }
+
+ // Push the new frames from the native stack
+ for (int jj = ii; jj < mTop; ++jj) {
+ mFrames[jj].push(jj, time, this);
+ }
+ break;
+ }
+}
+
+template<class FRAME, class BASE>
+void CallStack<FRAME, BASE>::showStack()
+{
+ fprintf(stderr, "mTop: %d skippedTime: %llu\n", mTop, mSkippedTime);
+ for (int ii = 0; ii < mTop; ++ii) {
+ fprintf(stderr, " %d: t %d gt %d f %x 0x%08x 0x%08x %s\n",
+ ii, mFrames[ii].time, mFrames[ii].global_time,
+ mFrames[ii].flags,
+ mFrames[ii].addr, mFrames[ii].function->addr,
+ mFrames[ii].function->name);
+ }
+}
+
+template<class FRAME, class BASE>
+void CallStack<FRAME, BASE>::showSnapshotStack()
+{
+ fprintf(stderr, "mSnapshotTop: %d\n", mSnapshotTop);
+ for (int ii = 0; ii < mSnapshotTop; ++ii) {
+ fprintf(stderr, " %d: t %d f %x 0x%08x 0x%08x %s\n",
+ ii, mSnapshotFrames[ii].time, mSnapshotFrames[ii].flags,
+ mSnapshotFrames[ii].addr, mSnapshotFrames[ii].function->addr,
+ mSnapshotFrames[ii].function->name);
+ }
+}
+
+#endif /* CALL_STACK_H */
diff --git a/emulator/qtools/check_trace.cpp b/emulator/qtools/check_trace.cpp
new file mode 100644
index 0000000..d933a87
--- /dev/null
+++ b/emulator/qtools/check_trace.cpp
@@ -0,0 +1,61 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <assert.h>
+#include "trace_reader.h"
+#include "armdis.h"
+#include "parse_options.h"
+
+typedef TraceReader<> TraceReaderType;
+
+#include "parse_options-inl.h"
+
+static const uint32_t kOffsetThreshold = 0x100000;
+
+void Usage(const char *program)
+{
+ fprintf(stderr, "Usage: %s [options] trace_file elf_file\n", program);
+ OptionsUsage();
+}
+
+int main(int argc, char **argv) {
+ // Parse the options
+ ParseOptions(argc, argv);
+ if (argc - optind != 2) {
+ Usage(argv[0]);
+ exit(1);
+ }
+
+ char *trace_filename = argv[optind++];
+ char *elf_file = argv[optind++];
+ TraceReader<> *trace = new TraceReader<>;
+ trace->Open(trace_filename);
+ trace->ReadKernelSymbols(elf_file);
+ trace->SetRoot(root);
+
+ while (1) {
+ symbol_type *sym;
+ BBEvent event;
+ BBEvent ignored;
+
+ if (GetNextValidEvent(trace, &event, &ignored, &sym))
+ break;
+ if (event.bb_num == 0)
+ break;
+ //printf("t%llu bb %lld %d\n", event.time, event.bb_num, event.num_insns);
+ uint64_t insn_time = trace->ReadInsnTime(event.time);
+ if (insn_time != event.time) {
+ printf("time: %llu insn time: %llu bb: %llu addr: 0x%x num_insns: %d, pid: %d\n",
+ event.time, insn_time, event.bb_num, event.bb_addr,
+ event.num_insns, event.pid);
+ exit(1);
+ }
+ for (int ii = 1; ii < event.num_insns; ++ii) {
+ trace->ReadInsnTime(event.time);
+ }
+ }
+
+ delete trace;
+ return 0;
+}
diff --git a/emulator/qtools/coverage.cpp b/emulator/qtools/coverage.cpp
new file mode 100644
index 0000000..fb1fe52
--- /dev/null
+++ b/emulator/qtools/coverage.cpp
@@ -0,0 +1,153 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <inttypes.h>
+#include "trace_reader.h"
+#include "parse_options.h"
+#include "opcode.h"
+
+const int kMillion = 1000000;
+const int kMHz = 200 * kMillion;
+
+struct symbol {
+ int numCalls; // number of times this function is called
+};
+
+typedef TraceReader<symbol> TraceReaderType;
+
+#include "parse_options-inl.h"
+#include "callstack.h"
+
+class MyFrame : public StackFrame<symbol_type> {
+ public:
+ void push(int stackLevel, uint64_t time, CallStackBase *base) {
+ function->numCalls += 1;
+ }
+ void pop(int stackLevel, uint64_t time, CallStackBase *base) {
+ }
+};
+
+typedef CallStack<MyFrame> CallStackType;
+
+static const int kNumStackFrames = 500;
+static const int kMaxThreads = (32 * 1024);
+CallStackType *stacks[kMaxThreads];
+
+// This comparison function is called from qsort() to sort symbols
+// into decreasing number of calls.
+int cmp_sym_calls(const void *a, const void *b) {
+ const symbol_type *syma, *symb;
+ uint64_t calls1, calls2;
+
+ syma = static_cast<symbol_type const *>(a);
+ symb = static_cast<symbol_type const *>(b);
+ calls1 = syma->numCalls;
+ calls2 = symb->numCalls;
+ if (calls1 < calls2)
+ return 1;
+ if (calls1 == calls2) {
+ int cmp = strcmp(syma->name, symb->name);
+ if (cmp == 0)
+ cmp = strcmp(syma->region->path, symb->region->path);
+ return cmp;
+ }
+ return -1;
+}
+
+// This comparison function is called from qsort() to sort symbols
+// into alphabetical order.
+int cmp_sym_names(const void *a, const void *b) {
+ const symbol_type *syma, *symb;
+
+ syma = static_cast<symbol_type const *>(a);
+ symb = static_cast<symbol_type const *>(b);
+ int cmp = strcmp(syma->region->path, symb->region->path);
+ if (cmp == 0)
+ cmp = strcmp(syma->name, symb->name);
+ return cmp;
+}
+
+void Usage(const char *program)
+{
+ fprintf(stderr, "Usage: %s [options] trace_file elf_file\n", program);
+ OptionsUsage();
+}
+
+int main(int argc, char **argv)
+{
+ ParseOptions(argc, argv);
+ if (argc - optind != 2) {
+ Usage(argv[0]);
+ exit(1);
+ }
+
+ char *trace_filename = argv[optind++];
+ char *elf_file = argv[optind++];
+ TraceReader<symbol> *trace = new TraceReader<symbol>;
+ trace->Open(trace_filename);
+ trace->SetDemangle(demangle);
+ trace->ReadKernelSymbols(elf_file);
+ trace->SetRoot(root);
+
+ BBEvent event;
+ while (1) {
+ BBEvent ignored;
+ symbol_type *function;
+
+ if (GetNextValidEvent(trace, &event, &ignored, &function))
+ break;
+ if (event.bb_num == 0)
+ break;
+
+ // Get the stack for the current thread
+ CallStackType *pStack = stacks[event.pid];
+
+ // If the stack does not exist, then allocate a new one.
+ if (pStack == NULL) {
+ pStack = new CallStackType(event.pid, kNumStackFrames, trace);
+ stacks[event.pid] = pStack;
+ }
+
+ // Update the stack
+ pStack->updateStack(&event, function);
+ }
+
+ for (int ii = 0; ii < kMaxThreads; ++ii) {
+ if (stacks[ii])
+ stacks[ii]->popAll(event.time);
+ }
+
+ int nsyms;
+ symbol_type *syms = trace->GetSymbols(&nsyms);
+
+ // Sort the symbols into decreasing number of calls
+ qsort(syms, nsyms, sizeof(symbol_type), cmp_sym_names);
+
+ symbol_type *psym = syms;
+ for (int ii = 0; ii < nsyms; ++ii, ++psym) {
+ // Ignore functions with non-zero calls
+ if (psym->numCalls)
+ continue;
+
+ // Ignore some symbols
+ if (strcmp(psym->name, "(end)") == 0)
+ continue;
+ if (strcmp(psym->name, "(unknown)") == 0)
+ continue;
+ if (strcmp(psym->name, ".plt") == 0)
+ continue;
+ char *ksym = " ";
+ if (psym->region->flags & region_type::kIsKernelRegion)
+ ksym = "k";
+ printf("%s %s %s\n", ksym, psym->name, psym->region->path);
+#if 0
+ printf("#%d %5d %s %s %s\n", ii + 1, psym->numCalls, ksym, psym->name,
+ psym->region->path);
+#endif
+ }
+ delete[] syms;
+ delete trace;
+
+ return 0;
+}
diff --git a/emulator/qtools/decoder.cpp b/emulator/qtools/decoder.cpp
new file mode 100644
index 0000000..ec53181
--- /dev/null
+++ b/emulator/qtools/decoder.cpp
@@ -0,0 +1,278 @@
+// Copyright 2006 The Android Open Source Project
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include "decoder.h"
+#include "trace_common.h"
+
+// This array provides a fast conversion from the initial byte in
+// a varint-encoded object to the length (in bytes) of that object.
+int prefix_to_len[] = {
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
+ 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 9, 9, 17, 17
+};
+
+// This array provides a fast conversion from the initial byte in
+// a varint-encoded object to the initial data bits for that object.
+int prefix_to_data[] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
+ 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
+ 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
+ 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
+ 112, 113, 114, 115, 116, 117, 118, 119,
+ 120, 121, 122, 123, 124, 125, 126, 127,
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 0, 0, 0, 0
+};
+
+signed char prefix_to_signed_data[] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
+ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
+ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
+ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff,
+ 0x00, 0x01, 0x02, 0x03, 0xfc, 0xfd, 0xfe, 0xff,
+ 0x00, 0x01, 0xfe, 0xff, 0x00, 0xff, 0x00, 0xff,
+};
+
+Decoder::Decoder()
+{
+ filename_ = NULL;
+ fstream_ = NULL;
+ next_ = NULL;
+ end_ = NULL;
+}
+
+Decoder::~Decoder()
+{
+ Close();
+ delete[] filename_;
+}
+
+void Decoder::Close()
+{
+ if (fstream_) {
+ fclose(fstream_);
+ fstream_ = NULL;
+ }
+}
+
+void Decoder::Open(char *filename)
+{
+ if (filename_ != NULL) {
+ delete[] filename_;
+ }
+ filename_ = new char[strlen(filename) + 1];
+ strcpy(filename_, filename);
+ fstream_ = fopen(filename_, "r");
+ if (fstream_ == NULL) {
+ perror(filename_);
+ exit(1);
+ }
+
+ int rval = fread(buf_, 1, kBufSize, fstream_);
+ if (rval != kBufSize) {
+ if (ferror(fstream_)) {
+ perror(filename_);
+ exit(1);
+ }
+ if (!feof(fstream_)) {
+ fprintf(stderr, "Unexpected short fread() before eof\n");
+ exit(1);
+ }
+ }
+ next_ = buf_;
+ end_ = buf_ + rval;
+}
+
+void Decoder::FillBuffer()
+{
+ assert(next_ <= end_);
+
+ if (end_ - next_ < kDecodingSpace && end_ == &buf_[kBufSize]) {
+ // Copy the unused bytes left at the end to the beginning of the
+ // buffer.
+ int len = end_ - next_;
+ if (len > 0)
+ memcpy(buf_, next_, len);
+
+ // Read enough bytes to fill up the buffer, if possible.
+ int nbytes = kBufSize - len;
+ int rval = fread(buf_ + len, 1, nbytes, fstream_);
+ if (rval < nbytes) {
+ if (ferror(fstream_)) {
+ perror(filename_);
+ exit(1);
+ }
+ if (!feof(fstream_)) {
+ fprintf(stderr, "Unexpected short fread() before eof\n");
+ exit(1);
+ }
+ }
+ end_ = &buf_[len + rval];
+ next_ = buf_;
+ }
+}
+
+void Decoder::Read(char *dest, int len)
+{
+ while (len > 0) {
+ int nbytes = end_ - next_;
+ if (nbytes == 0) {
+ FillBuffer();
+ nbytes = end_ - next_;
+ if (nbytes == 0)
+ break;
+ }
+ if (nbytes > len)
+ nbytes = len;
+ memcpy(dest, next_, nbytes);
+ dest += nbytes;
+ len -= nbytes;
+ next_ += nbytes;
+ }
+}
+
+// Decode a varint-encoded object starting at the current position in
+// the array "buf_" and return the decoded value as a 64-bit integer.
+// A varint-encoded object has an initial prefix that specifies how many
+// data bits follow. If the first bit is zero, for example, then there
+// are 7 data bits that follow. The table below shows the prefix values
+// and corresponding data bits.
+//
+// Prefix Bytes Data bits
+// 0 1 7
+// 10 2 14
+// 110 3 21
+// 1110 4 28
+// 11110 5 35
+// 111110 6 42
+// 11111100 9 64
+// 11111101 reserved
+// 11111110 reserved
+// 11111111 reserved
+int64_t Decoder::Decode(bool is_signed)
+{
+ int64_t val64;
+
+ if (end_ - next_ < kDecodingSpace)
+ FillBuffer();
+
+#if BYTE_ORDER == BIG_ENDIAN
+ uint8_t byte0 = *next_;
+
+ // Get the number of bytes to decode based on the first byte.
+ int len = prefix_to_len[byte0];
+
+ if (next_ + len > end_) {
+ fprintf(stderr, "%s: decoding past end of file.\n", filename_);
+ exit(1);
+ }
+
+ // Get the first data byte.
+ if (is_signed)
+ val64 = prefix_to_signed_data[byte0];
+ else
+ val64 = prefix_to_data[byte0];
+
+ next_ += 1;
+ for (int ii = 1; ii < len; ++ii) {
+ val64 = (val64 << 8) | *next_++;
+ }
+#else
+ // If we are on a little-endian machine, then use large, unaligned loads.
+ uint64_t data = *(reinterpret_cast<uint64_t*>(next_));
+ uint8_t byte0 = data;
+ data = bswap64(data);
+
+ // Get the number of bytes to decode based on the first byte.
+ int len = prefix_to_len[byte0];
+
+ if (next_ + len > end_) {
+ fprintf(stderr, "%s: decoding past end of file.\n", filename_);
+ exit(1);
+ }
+
+ // Get the first data byte.
+ if (is_signed)
+ val64 = prefix_to_signed_data[byte0];
+ else
+ val64 = prefix_to_data[byte0];
+
+ switch (len) {
+ case 1:
+ break;
+ case 2:
+ val64 = (val64 << 8) | ((data >> 48) & 0xffull);
+ break;
+ case 3:
+ val64 = (val64 << 16) | ((data >> 40) & 0xffffull);
+ break;
+ case 4:
+ val64 = (val64 << 24) | ((data >> 32) & 0xffffffull);
+ break;
+ case 5:
+ val64 = (val64 << 32) | ((data >> 24) & 0xffffffffull);
+ break;
+ case 6:
+ val64 = (val64 << 40) | ((data >> 16) & 0xffffffffffull);
+ break;
+ case 9:
+ data = *(reinterpret_cast<uint64_t*>(&next_[1]));
+ val64 = bswap64(data);
+ break;
+ }
+ next_ += len;
+#endif
+ return val64;
+}
diff --git a/emulator/qtools/decoder.h b/emulator/qtools/decoder.h
new file mode 100644
index 0000000..44905fd
--- /dev/null
+++ b/emulator/qtools/decoder.h
@@ -0,0 +1,28 @@
+// Copyright 2006 The Android Open Source Project
+
+#include <stdio.h>
+#include <inttypes.h>
+
+class Decoder {
+ public:
+ Decoder();
+ ~Decoder();
+
+ void Open(char *filename);
+ void Close();
+ int64_t Decode(bool is_signed);
+ void Read(char *dest, int len);
+ bool IsEOF() { return (end_ == next_) && feof(fstream_); }
+
+ private:
+ static const int kBufSize = 4096;
+ static const int kDecodingSpace = 9;
+
+ void FillBuffer();
+
+ char *filename_;
+ FILE *fstream_;
+ uint8_t buf_[kBufSize];
+ uint8_t *next_;
+ uint8_t *end_;
+};
diff --git a/emulator/qtools/dmtrace.cpp b/emulator/qtools/dmtrace.cpp
new file mode 100644
index 0000000..a27193a
--- /dev/null
+++ b/emulator/qtools/dmtrace.cpp
@@ -0,0 +1,253 @@
+// Copyright 2006 The Android Open Source Project
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <string.h>
+#include "dmtrace.h"
+
+static const short kVersion = 2;
+
+const DmTrace::Header DmTrace::header = {
+ 0x574f4c53, kVersion, sizeof(DmTrace::Header), 0LL
+};
+
+static char *keyHeader = "*version\n" "2\n" "clock=thread-cpu\n";
+static char *keyThreadHeader = "*threads\n";
+static char *keyFunctionHeader = "*methods\n";
+static char *keyEnd = "*end\n";
+
+DmTrace::DmTrace() {
+ fData = NULL;
+ fTrace = NULL;
+ threads = new std::vector<ThreadRecord*>;
+ functions = new std::vector<FunctionRecord*>;
+}
+
+DmTrace::~DmTrace() {
+ delete threads;
+ delete functions;
+}
+
+void DmTrace::open(const char *dmtrace_file, uint64_t start_time)
+{
+ fTrace = fopen(dmtrace_file, "w");
+ if (fTrace == NULL) {
+ perror(dmtrace_file);
+ exit(1);
+ }
+
+ // Make a temporary file to write the data into.
+ char tmpData[32];
+ strcpy(tmpData, "/tmp/dmtrace-data-XXXXXX");
+ int data_fd = mkstemp(tmpData);
+ if (data_fd < 0) {
+ perror("Cannot create temporary file");
+ exit(1);
+ }
+
+ // Ensure it goes away on exit.
+ unlink(tmpData);
+ fData = fdopen(data_fd, "w+");
+ if (fData == NULL) {
+ perror("Can't make temp data file");
+ exit(1);
+ }
+
+ writeHeader(fData, start_time);
+}
+
+void DmTrace::close()
+{
+ if (fTrace == NULL)
+ return;
+ writeKeyFile(fTrace);
+
+ // Take down how much data we wrote to the temp data file.
+ long size = ftell(fData);
+ // Rewind the data file and append its contents to the trace file.
+ rewind(fData);
+ char *data = (char *)malloc(size);
+ fread(data, size, 1, fData);
+ fwrite(data, size, 1, fTrace);
+ free(data);
+ fclose(fData);
+ fclose(fTrace);
+}
+
+/*
+ * Write values to the binary data file.
+ */
+void DmTrace::write2LE(FILE* fstream, unsigned short val)
+{
+ putc(val & 0xff, fstream);
+ putc(val >> 8, fstream);
+}
+
+void DmTrace::write4LE(FILE* fstream, unsigned int val)
+{
+ putc(val & 0xff, fstream);
+ putc((val >> 8) & 0xff, fstream);
+ putc((val >> 16) & 0xff, fstream);
+ putc((val >> 24) & 0xff, fstream);
+}
+
+void DmTrace::write8LE(FILE* fstream, unsigned long long val)
+{
+ putc(val & 0xff, fstream);
+ putc((val >> 8) & 0xff, fstream);
+ putc((val >> 16) & 0xff, fstream);
+ putc((val >> 24) & 0xff, fstream);
+ putc((val >> 32) & 0xff, fstream);
+ putc((val >> 40) & 0xff, fstream);
+ putc((val >> 48) & 0xff, fstream);
+ putc((val >> 56) & 0xff, fstream);
+}
+
+void DmTrace::writeHeader(FILE *fstream, uint64_t startTime)
+{
+ write4LE(fstream, header.magic);
+ write2LE(fstream, header.version);
+ write2LE(fstream, header.offset);
+ write8LE(fstream, startTime);
+}
+
+void DmTrace::writeDataRecord(FILE *fstream, int threadId,
+ unsigned int methodVal,
+ unsigned int elapsedTime)
+{
+ write2LE(fstream, threadId);
+ write4LE(fstream, methodVal);
+ write4LE(fstream, elapsedTime);
+}
+
+void DmTrace::addFunctionEntry(int functionId, uint32_t cycle, uint32_t pid)
+{
+ writeDataRecord(fData, pid, functionId, cycle);
+}
+
+void DmTrace::addFunctionExit(int functionId, uint32_t cycle, uint32_t pid)
+{
+ writeDataRecord(fData, pid, functionId | 1, cycle);
+}
+
+void DmTrace::addFunction(int functionId, const char *name)
+{
+ FunctionRecord *rec = new FunctionRecord;
+ rec->id = functionId;
+ rec->name = name;
+ functions->push_back(rec);
+}
+
+void DmTrace::addFunction(int functionId, const char *clazz,
+ const char *method, const char *sig)
+{
+ // Allocate space for all the strings, plus 2 tab separators plus null byte.
+ // We currently don't reclaim this space.
+ int len = strlen(clazz) + strlen(method) + strlen(sig) + 3;
+ char *name = new char[len];
+ sprintf(name, "%s\t%s\t%s", clazz, method, sig);
+
+ addFunction(functionId, name);
+}
+
+void DmTrace::parseAndAddFunction(int functionId, const char *name)
+{
+ // Parse the "name" string into "class", "method" and "signature".
+ // The "name" string should look something like this:
+ // name = "java.util.LinkedList.size()I"
+ // and it will be parsed into this:
+ // clazz = "java.util.LinkedList"
+ // method = "size"
+ // sig = "()I"
+
+ // Find the first parenthesis, the start of the signature.
+ char *paren = strchr(name, '(');
+
+ // If not found, then add the original name.
+ if (paren == NULL) {
+ addFunction(functionId, name);
+ return;
+ }
+
+ // Copy the signature
+ int len = strlen(paren) + 1;
+ char *sig = new char[len];
+ strcpy(sig, paren);
+
+ // Zero the parenthesis so that we can search backwards from the signature
+ *paren = 0;
+
+ // Search for the last period, the start of the method name
+ char *dot = strrchr(name, '.');
+
+ // If not found, then add the original name.
+ if (dot == NULL || dot == name) {
+ delete[] sig;
+ *paren = '(';
+ addFunction(functionId, name);
+ return;
+ }
+
+ // Copy the method, not including the dot
+ len = strlen(dot + 1) + 1;
+ char *method = new char[len];
+ strcpy(method, dot + 1);
+
+ // Zero the dot to delimit the class name
+ *dot = 0;
+
+ addFunction(functionId, name, method, sig);
+
+ // Free the space we allocated.
+ delete[] sig;
+ delete[] method;
+}
+
+void DmTrace::addThread(int threadId, const char *name)
+{
+ ThreadRecord *rec = new ThreadRecord;
+ rec->id = threadId;
+ rec->name = name;
+ threads->push_back(rec);
+}
+
+void DmTrace::updateName(int threadId, const char *name)
+{
+ std::vector<ThreadRecord*>::iterator iter;
+
+ for (iter = threads->begin(); iter != threads->end(); ++iter) {
+ if ((*iter)->id == threadId) {
+ (*iter)->name = name;
+ return;
+ }
+ }
+}
+
+void DmTrace::writeKeyFile(FILE *fstream)
+{
+ fwrite(keyHeader, strlen(keyHeader), 1, fstream);
+ writeThreads(fstream);
+ writeFunctions(fstream);
+ fwrite(keyEnd, strlen(keyEnd), 1, fstream);
+}
+
+void DmTrace::writeThreads(FILE *fstream)
+{
+ std::vector<ThreadRecord*>::iterator iter;
+
+ fwrite(keyThreadHeader, strlen(keyThreadHeader), 1, fstream);
+ for (iter = threads->begin(); iter != threads->end(); ++iter) {
+ fprintf(fstream, "%d\t%s\n", (*iter)->id, (*iter)->name);
+ }
+}
+
+void DmTrace::writeFunctions(FILE *fstream)
+{
+ std::vector<FunctionRecord*>::iterator iter;
+
+ fwrite(keyFunctionHeader, strlen(keyFunctionHeader), 1, fstream);
+ for (iter = functions->begin(); iter != functions->end(); ++iter) {
+ fprintf(fstream, "0x%x\t%s\n", (*iter)->id, (*iter)->name);
+ }
+}
diff --git a/emulator/qtools/dmtrace.h b/emulator/qtools/dmtrace.h
new file mode 100644
index 0000000..6e20921
--- /dev/null
+++ b/emulator/qtools/dmtrace.h
@@ -0,0 +1,61 @@
+// Copyright 2006 The Android Open Source Project
+
+#ifndef DMTRACE_H
+#define DMTRACE_H
+
+#include <vector>
+
+class DmTrace {
+ public:
+ struct Header {
+ uint32_t magic;
+ uint16_t version;
+ uint16_t offset;
+ uint64_t date_time;
+ };
+
+ DmTrace();
+ ~DmTrace();
+
+ void open(const char *dmtrace_file, uint64_t startTime);
+ void close();
+ void addFunctionEntry(int methodId, uint32_t cycle, uint32_t pid);
+ void addFunctionExit(int methodId, uint32_t cycle, uint32_t pid);
+ void addFunction(int functionId, const char *name);
+ void addFunction(int functionId, const char *clazz, const char *method,
+ const char *sig);
+ void parseAndAddFunction(int functionId, const char *name);
+ void addThread(int threadId, const char *name);
+ void updateName(int threadId, const char *name);
+
+ private:
+ static const Header header;
+
+ struct ThreadRecord {
+ int id;
+ const char *name;
+ };
+
+ struct FunctionRecord {
+ int id;
+ const char *name;
+ };
+
+ void write2LE(FILE* fstream, unsigned short val);
+ void write4LE(FILE* fstream, unsigned int val);
+ void write8LE(FILE* fstream, unsigned long long val);
+ void writeHeader(FILE *fstream, uint64_t startTime);
+ void writeDataRecord(FILE *fstream, int threadId,
+ unsigned int methodVal,
+ unsigned int elapsedTime);
+ void writeKeyFile(FILE *fstream);
+ void writeThreads(FILE *fstream);
+ void writeFunctions(FILE *fstream);
+
+ FILE *fData;
+ FILE *fTrace;
+ std::vector<ThreadRecord*> *threads;
+ std::vector<FunctionRecord*> *functions;
+};
+
+#endif // DMTRACE_H
diff --git a/emulator/qtools/exc_dump.cpp b/emulator/qtools/exc_dump.cpp
new file mode 100644
index 0000000..166586f
--- /dev/null
+++ b/emulator/qtools/exc_dump.cpp
@@ -0,0 +1,28 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include "trace_reader_base.h"
+
+int main(int argc, char **argv) {
+ if (argc != 2) {
+ fprintf(stderr, "Usage: %s trace_file\n", argv[0]);
+ exit(1);
+ }
+
+ char *trace_filename = argv[1];
+ TraceReaderBase *trace = new TraceReaderBase;
+ trace->Open(trace_filename);
+
+ while (1) {
+ uint64_t time, recnum, bb_num, bb_start_time;
+ uint32_t pc, target_pc;
+ int num_insns;
+
+ if (trace->ReadExc(&time, &pc, &recnum, &target_pc, &bb_num,
+ &bb_start_time, &num_insns))
+ break;
+ printf("time: %lld rec: %llu pc: %08x target: %08x bb: %llu bb_start: %llu insns: %d\n",
+ time, recnum, pc, target_pc, bb_num, bb_start_time, num_insns);
+ }
+ return 0;
+}
diff --git a/emulator/qtools/gtrace.cpp b/emulator/qtools/gtrace.cpp
new file mode 100644
index 0000000..673d8a4
--- /dev/null
+++ b/emulator/qtools/gtrace.cpp
@@ -0,0 +1,152 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include "gtrace.h"
+
+// A buffer of zeros
+static char zeros[Gtrace::kGtraceEntriesPerBlock * sizeof(Gtrace::trace_entry)];
+
+Gtrace::Gtrace() {
+ gtrace_file_ = NULL;
+ ftrace_ = NULL;
+ fnames_ = NULL;
+ start_sec_ = 0;
+ pdate_ = 0;
+ ptime_ = 0;
+ num_entries_ = 0;
+ blockno_ = 1;
+ current_pid_ = 0;
+}
+
+Gtrace::~Gtrace() {
+ if (ftrace_) {
+ // Extend the trace file to a multiple of 8k. Otherwise gtracepost64
+ // complains.
+ long pos = ftell(ftrace_);
+ long pos_end = (pos + 0x1fff) & ~0x1fff;
+ if (pos_end > pos) {
+ char ch = 0;
+ fseek(ftrace_, pos_end - 1, SEEK_SET);
+ fwrite(&ch, 1, 1, ftrace_);
+ }
+ fclose(ftrace_);
+ }
+ if (fnames_)
+ fclose(fnames_);
+}
+
+void Gtrace::Open(const char *gtrace_file, uint32_t pdate, uint32_t ptime)
+{
+ ftrace_ = fopen(gtrace_file, "w");
+ if (ftrace_ == NULL) {
+ perror(gtrace_file);
+ exit(1);
+ }
+ gtrace_file_ = gtrace_file;
+
+ pdate_ = pdate;
+ ptime_ = ptime;
+ sprintf(gname_file_, "gname_%x_%06x.txt", pdate, ptime);
+ fnames_ = fopen(gname_file_, "w");
+ if (fnames_ == NULL) {
+ perror(gname_file_);
+ exit(1);
+ }
+ fprintf(fnames_, "# File# Proc# Line# Name\n");
+}
+
+void Gtrace::WriteFirstHeader(uint32_t start_sec, uint32_t pid)
+{
+ first_header fh;
+ current_pid_ = pid;
+ start_sec_ = start_sec;
+ FillFirstHeader(start_sec, pid, &fh);
+ fwrite(&fh, sizeof(fh), 1, ftrace_);
+ num_entries_ = 8;
+}
+
+void Gtrace::FillFirstHeader(uint32_t start_sec, uint32_t pid,
+ first_header *fh) {
+ int cpu = 0;
+ int max_files = 16;
+ int max_procedures = 12;
+
+ fh->common.blockno = 0;
+ fh->common.entry_width = 8;
+ fh->common.block_tic = kBaseTic;
+ fh->common.block_time = start_sec;
+ //fh->common.usec_cpu = (start_usec << 8) | (cpu & 0xff);
+ fh->common.usec_cpu = cpu & 0xff;
+ fh->common.pid = pid;
+ fh->common.bug_count = 0;
+ fh->common.zero_count = 0;
+
+ fh->tic = kBaseTic + 1;
+ fh->one = 1;
+ fh->tics_per_second = kTicsPerSecond;
+ fh->trace_time = start_sec;
+ fh->version = 5;
+ fh->file_proc = (max_files << 8) | max_procedures;
+ fh->pdate = pdate_;
+ fh->ptime = ptime_;
+}
+
+void Gtrace::WriteBlockHeader(uint32_t cycle, uint32_t pid)
+{
+ int cpu = 0;
+ block_header bh;
+
+ bh.blockno = blockno_++;
+ bh.entry_width = 8;
+ bh.block_tic = cycle + kBaseTic;
+ bh.block_time = start_sec_ + cycle / kTicsPerSecond;
+ //bh.usec_cpu = (start_usec << 8) | (cpu & 0xff);
+ bh.usec_cpu = cpu & 0xff;
+ bh.pid = pid;
+ bh.bug_count = 0;
+ bh.zero_count = 0;
+ fwrite(&bh, sizeof(bh), 1, ftrace_);
+}
+
+void Gtrace::AddGtraceRecord(int filenum, int procnum, uint32_t cycle, uint32_t pid,
+ int is_exit)
+{
+ trace_entry entry;
+
+ if (current_pid_ != pid) {
+ current_pid_ = pid;
+
+ // We are switching to a new process id, so pad the current block
+ // with zeros.
+ int num_zeros = (kGtraceEntriesPerBlock - num_entries_) * sizeof(entry);
+ fwrite(zeros, num_zeros, 1, ftrace_);
+ WriteBlockHeader(cycle, pid);
+ num_entries_ = 4;
+ }
+
+ // If the current block is full, write out a new block header
+ if (num_entries_ == kGtraceEntriesPerBlock) {
+ WriteBlockHeader(cycle, pid);
+ num_entries_ = 4;
+ }
+
+ entry.cycle = cycle + kBaseTic;
+ entry.event = (filenum << 13) | (procnum << 1) | is_exit;
+ fwrite(&entry, sizeof(entry), 1, ftrace_);
+ num_entries_ += 1;
+}
+
+void Gtrace::AddProcEntry(int filenum, int procnum, uint32_t cycle, uint32_t pid)
+{
+ AddGtraceRecord(filenum, procnum, cycle, pid, 0);
+}
+
+void Gtrace::AddProcExit(int filenum, int procnum, uint32_t cycle, uint32_t pid)
+{
+ AddGtraceRecord(filenum, procnum, cycle, pid, 1);
+}
+
+void Gtrace::AddProcedure(int filenum, int procnum, const char *proc_name)
+{
+ fprintf(fnames_, "%d %d %d %s\n", filenum, procnum, procnum, proc_name);
+}
diff --git a/emulator/qtools/gtrace.h b/emulator/qtools/gtrace.h
new file mode 100644
index 0000000..542adc2
--- /dev/null
+++ b/emulator/qtools/gtrace.h
@@ -0,0 +1,69 @@
+// Copyright 2006 The Android Open Source Project
+
+#ifndef GTRACE_H
+#define GTRACE_H
+
+class Gtrace {
+ public:
+ static const int kGtraceEntriesPerBlock = 1024;
+ static const uint32_t kMillion = 1000000;
+ static const uint32_t kTicsPerSecond = 200 * kMillion;
+ static const int kBaseTic = 0x1000;
+
+ struct trace_entry {
+ uint32_t cycle;
+ uint32_t event;
+ };
+
+ struct block_header {
+ uint32_t blockno;
+ uint32_t entry_width;
+ uint32_t block_tic;
+ uint32_t block_time;
+ uint32_t usec_cpu;
+ uint32_t pid;
+ uint32_t bug_count;
+ uint32_t zero_count;
+ };
+
+ struct first_header {
+ block_header common;
+ uint32_t tic;
+ uint32_t one;
+ uint32_t tics_per_second;
+ uint32_t trace_time;
+ uint32_t version;
+ uint32_t file_proc;
+ uint32_t pdate;
+ uint32_t ptime;
+ };
+
+ Gtrace();
+ ~Gtrace();
+
+ void Open(const char *gtrace_file, uint32_t pdate, uint32_t ptime);
+ void WriteFirstHeader(uint32_t start_sec, uint32_t pid);
+ void AddProcedure(int filenum, int procnum, const char *proc_name);
+ void AddProcEntry(int filenum, int procnum, uint32_t cycle, uint32_t pid);
+ void AddProcExit(int filenum, int procnum, uint32_t cycle, uint32_t pid);
+
+ private:
+ void AddGtraceRecord(int filenum, int procnum, uint32_t cycle, uint32_t pid,
+ int is_exit);
+ void FillFirstHeader(uint32_t start_sec, uint32_t pid,
+ first_header *fh);
+ void WriteBlockHeader(uint32_t cycle, uint32_t pid);
+
+ const char *gtrace_file_;
+ char gname_file_[100];
+ FILE *ftrace_;
+ FILE *fnames_;
+ uint32_t start_sec_;
+ uint32_t pdate_;
+ uint32_t ptime_;
+ int num_entries_;
+ int blockno_;
+ uint32_t current_pid_;
+};
+
+#endif // GTRACE_H
diff --git a/emulator/qtools/hash_table.h b/emulator/qtools/hash_table.h
new file mode 100644
index 0000000..45786ec
--- /dev/null
+++ b/emulator/qtools/hash_table.h
@@ -0,0 +1,193 @@
+// Copyright 2006 The Android Open Source Project
+
+#ifndef HASH_TABLE_H
+#define HASH_TABLE_H
+
+#include <string.h>
+#include <inttypes.h>
+
+template<class T>
+class HashTable {
+ public:
+ HashTable(int size, T default_value = T());
+ ~HashTable();
+
+ typedef struct entry {
+ entry *next;
+ char *key;
+ T value;
+ } entry_type;
+
+ typedef T value_type;
+
+ void Update(const char *key, T value);
+ T Find(const char *key);
+ entry_type* GetFirst();
+ entry_type* GetNext();
+
+ private:
+ uint32_t HashFunction(const char *key);
+
+ int size_;
+ int mask_;
+ T default_value_;
+ entry_type **table_;
+ int num_entries_;
+ int current_index_;
+ entry_type *current_ptr_;
+};
+
+template<class T>
+HashTable<T>::HashTable(int size, T default_value)
+{
+ int pow2;
+
+ // Round up size to a power of two
+ for (pow2 = 2; pow2 < size; pow2 <<= 1)
+ ; // empty body
+
+ size_ = pow2;
+ mask_ = pow2 - 1;
+ default_value_ = default_value;
+
+ // Allocate a table of pointers and initialize them all to NULL.
+ table_ = new entry_type*[size_];
+ for (int ii = 0; ii < size_; ++ii)
+ table_[ii] = NULL;
+ num_entries_ = 0;
+ current_index_ = 0;
+ current_ptr_ = NULL;
+}
+
+template<class T>
+HashTable<T>::~HashTable()
+{
+ for (int ii = 0; ii < size_; ++ii) {
+ entry_type *ptr, *next;
+
+ // Delete all the pointers in the chain at this table position.
+ // Save the next pointer before deleting each entry so that we
+ // don't dereference part of a deallocated object.
+ for (ptr = table_[ii]; ptr; ptr = next) {
+ next = ptr->next;
+ delete[] ptr->key;
+ delete ptr;
+ }
+ }
+ delete[] table_;
+}
+
+// Professor Daniel J. Bernstein's hash function. See
+// http://www.partow.net/programming/hashfunctions/
+template<class T>
+uint32_t HashTable<T>::HashFunction(const char *key)
+{
+ uint32_t hash = 5381;
+
+ int len = strlen(key);
+ for(int ii = 0; ii < len; ++key, ++ii)
+ hash = ((hash << 5) + hash) + *key;
+
+ return hash;
+}
+
+template<class T>
+void HashTable<T>::Update(const char *key, T value)
+{
+ // Hash the key to get the table position
+ int len = strlen(key);
+ int pos = HashFunction(key) & mask_;
+
+ // Search the chain for a matching key
+ for (entry_type *ptr = table_[pos]; ptr; ptr = ptr->next) {
+ if (strcmp(ptr->key, key) == 0) {
+ ptr->value = value;
+ return;
+ }
+ }
+
+ // Create a new hash entry and fill in the values
+ entry_type *ptr = new entry_type;
+
+ // Copy the string
+ ptr->key = new char[len + 1];
+ strcpy(ptr->key, key);
+ ptr->value = value;
+
+ // Insert the new entry at the beginning of the list
+ ptr->next = table_[pos];
+ table_[pos] = ptr;
+ num_entries_ += 1;
+}
+
+template<class T>
+typename HashTable<T>::value_type HashTable<T>::Find(const char *key)
+{
+ // Hash the key to get the table position
+ int pos = HashFunction(key) & mask_;
+
+ // Search the chain for a matching key
+ for (entry_type *ptr = table_[pos]; ptr; ptr = ptr->next) {
+ if (strcmp(ptr->key, key) == 0)
+ return ptr->value;
+ }
+
+ // If we get here, then we didn't find the key
+ return default_value_;
+}
+
+template<class T>
+typename HashTable<T>::entry_type* HashTable<T>::GetFirst()
+{
+ // Find the first non-NULL table entry.
+ for (current_index_ = 0; current_index_ < size_; ++current_index_) {
+ if (table_[current_index_])
+ break;
+ }
+
+ // If there are no table entries, then return NULL.
+ if (current_index_ == size_)
+ return NULL;
+
+ // Remember and return the current element.
+ current_ptr_ = table_[current_index_];
+ return current_ptr_;
+}
+
+template<class T>
+typename HashTable<T>::entry_type* HashTable<T>::GetNext()
+{
+ // If we already iterated part way through the hash table, then continue
+ // to the next element.
+ if (current_ptr_) {
+ current_ptr_ = current_ptr_->next;
+
+ // If we are pointing to a valid element, then return it.
+ if (current_ptr_)
+ return current_ptr_;
+
+ // Otherwise, start searching at the next table index.
+ current_index_ += 1;
+ }
+
+ // Find the next non-NULL table entry.
+ for (; current_index_ < size_; ++current_index_) {
+ if (table_[current_index_])
+ break;
+ }
+
+ // If there are no more non-NULL table entries, then return NULL.
+ if (current_index_ == size_) {
+ // Reset the current index so that we will start over from the
+ // beginning on the next call to GetNext().
+ current_index_ = 0;
+ return NULL;
+ }
+
+ // Remember and return the current element.
+ current_ptr_ = table_[current_index_];
+ return current_ptr_;
+}
+
+
+#endif // HASH_TABLE_H
diff --git a/emulator/qtools/hist_trace.cpp b/emulator/qtools/hist_trace.cpp
new file mode 100644
index 0000000..d2c6a90
--- /dev/null
+++ b/emulator/qtools/hist_trace.cpp
@@ -0,0 +1,64 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include "trace_reader.h"
+
+static const int kMaxHistEntries = 256;
+static const int kMaxHistEntries2 = kMaxHistEntries / 2;
+int hist[kMaxHistEntries];
+int underflow, overflow;
+
+int main(int argc, char **argv) {
+ if (argc != 2) {
+ fprintf(stderr, "Usage: %s trace_file\n", argv[0]);
+ exit(1);
+ }
+
+ char *trace_filename = argv[1];
+ TraceReaderBase *trace = new TraceReaderBase;
+ trace->Open(trace_filename);
+
+ uint64_t prev_bb_num = 0;
+ uint64_t prev_time = 0;
+ int total = 0;
+
+ while (1) {
+ BBEvent event;
+
+ if (trace->ReadBB(&event))
+ break;
+ int bb_diff = event.bb_num - prev_bb_num;
+ //int time_diff = event.time - prev_time;
+ //printf("bb_num: %llu prev: %llu, diff: %d\n",
+ // event.bb_num, prev_bb_num, bb_diff);
+ prev_bb_num = event.bb_num;
+ prev_time = event.time;
+
+ bb_diff += kMaxHistEntries2;
+ if (bb_diff < 0)
+ underflow += 1;
+ else if (bb_diff >= kMaxHistEntries)
+ overflow += 1;
+ else
+ hist[bb_diff] += 1;
+ total += 1;
+ }
+
+ int sum = 0;
+ double sum_per = 0;
+ double per = 0;
+ for (int ii = 0; ii < kMaxHistEntries; ++ii) {
+ if (hist[ii] == 0)
+ continue;
+ per = 100.0 * hist[ii] / total;
+ sum += hist[ii];
+ sum_per = 100.0 * sum / total;
+ printf(" %4d: %6d %6.2f %6.2f\n", ii - kMaxHistEntries2, hist[ii], per, sum_per);
+ }
+ per = 100.0 * underflow / total;
+ printf("under: %6d %6.2f\n", underflow, per);
+ per = 100.0 * overflow / total;
+ printf("over: %6d %6.2f\n", overflow, per);
+ printf("total: %6d\n", total);
+ return 0;
+}
diff --git a/emulator/qtools/opcode.cpp b/emulator/qtools/opcode.cpp
new file mode 100644
index 0000000..41bef3a
--- /dev/null
+++ b/emulator/qtools/opcode.cpp
@@ -0,0 +1,204 @@
+// Copyright 2006 The Android Open Source Project
+
+#include <stdio.h>
+#include <inttypes.h>
+#include "opcode.h"
+
+// Note: this array depends on the Opcode enum defined in opcode.h
+uint32_t opcode_flags[] = {
+ 0, // OP_INVALID
+ 0, // OP_UNDEFINED
+ kCatAlu, // OP_ADC
+ kCatAlu, // OP_ADD
+ kCatAlu, // OP_AND
+ kCatBranch, // OP_B
+ kCatBranch | kCatBranchLink, // OP_BL
+ kCatAlu, // OP_BIC
+ 0, // OP_BKPT
+ kCatBranch | kCatBranchLink | kCatBranchExch, // OP_BLX
+ kCatBranch | kCatBranchExch, // OP_BX
+ kCatCoproc, // OP_CDP
+ kCatAlu, // OP_CLZ
+ kCatAlu, // OP_CMN
+ kCatAlu, // OP_CMP
+ kCatAlu, // OP_EOR
+ kCatCoproc | kCatLoad, // OP_LDC
+ kCatLoad | kCatMultiple, // OP_LDM
+ kCatLoad | kCatWord, // OP_LDR
+ kCatLoad | kCatByte, // OP_LDRB
+ kCatLoad | kCatByte, // OP_LDRBT
+ kCatLoad | kCatHalf, // OP_LDRH
+ kCatLoad | kCatByte | kCatSigned, // OP_LDRSB
+ kCatLoad | kCatHalf | kCatSigned, // OP_LDRSH
+ kCatLoad | kCatWord, // OP_LDRT
+ kCatCoproc, // OP_MCR
+ kCatAlu, // OP_MLA
+ kCatAlu, // OP_MOV
+ kCatCoproc, // OP_MRC
+ 0, // OP_MRS
+ 0, // OP_MSR
+ kCatAlu, // OP_MUL
+ kCatAlu, // OP_MVN
+ kCatAlu, // OP_ORR
+ 0, // OP_PLD
+ kCatAlu, // OP_RSB
+ kCatAlu, // OP_RSC
+ kCatAlu, // OP_SBC
+ kCatAlu, // OP_SMLAL
+ kCatAlu, // OP_SMULL
+ kCatCoproc | kCatStore, // OP_STC
+ kCatStore | kCatMultiple, // OP_STM
+ kCatStore | kCatWord, // OP_STR
+ kCatStore | kCatByte, // OP_STRB
+ kCatStore | kCatByte, // OP_STRBT
+ kCatStore | kCatHalf, // OP_STRH
+ kCatStore | kCatWord, // OP_STRT
+ kCatAlu, // OP_SUB
+ 0, // OP_SWI
+ kCatLoad | kCatStore, // OP_SWP
+ kCatLoad | kCatStore | kCatByte, // OP_SWPB
+ kCatAlu, // OP_TEQ
+ kCatAlu, // OP_TST
+ kCatAlu, // OP_UMLAL
+ kCatAlu, // OP_UMULL
+
+ 0, // OP_THUMB_UNDEFINED,
+ kCatAlu, // OP_THUMB_ADC,
+ kCatAlu, // OP_THUMB_ADD,
+ kCatAlu, // OP_THUMB_AND,
+ kCatAlu, // OP_THUMB_ASR,
+ kCatBranch, // OP_THUMB_B,
+ kCatAlu, // OP_THUMB_BIC,
+ 0, // OP_THUMB_BKPT,
+ kCatBranch | kCatBranchLink, // OP_THUMB_BL,
+ kCatBranch | kCatBranchLink | kCatBranchExch, // OP_THUMB_BLX,
+ kCatBranch | kCatBranchExch, // OP_THUMB_BX,
+ kCatAlu, // OP_THUMB_CMN,
+ kCatAlu, // OP_THUMB_CMP,
+ kCatAlu, // OP_THUMB_EOR,
+ kCatLoad | kCatMultiple, // OP_THUMB_LDMIA,
+ kCatLoad | kCatWord, // OP_THUMB_LDR,
+ kCatLoad | kCatByte, // OP_THUMB_LDRB,
+ kCatLoad | kCatHalf, // OP_THUMB_LDRH,
+ kCatLoad | kCatByte | kCatSigned, // OP_THUMB_LDRSB,
+ kCatLoad | kCatHalf | kCatSigned, // OP_THUMB_LDRSH,
+ kCatAlu, // OP_THUMB_LSL,
+ kCatAlu, // OP_THUMB_LSR,
+ kCatAlu, // OP_THUMB_MOV,
+ kCatAlu, // OP_THUMB_MUL,
+ kCatAlu, // OP_THUMB_MVN,
+ kCatAlu, // OP_THUMB_NEG,
+ kCatAlu, // OP_THUMB_ORR,
+ kCatLoad | kCatMultiple, // OP_THUMB_POP,
+ kCatStore | kCatMultiple, // OP_THUMB_PUSH,
+ kCatAlu, // OP_THUMB_ROR,
+ kCatAlu, // OP_THUMB_SBC,
+ kCatStore | kCatMultiple, // OP_THUMB_STMIA,
+ kCatStore | kCatWord, // OP_THUMB_STR,
+ kCatStore | kCatByte, // OP_THUMB_STRB,
+ kCatStore | kCatHalf, // OP_THUMB_STRH,
+ kCatAlu, // OP_THUMB_SUB,
+ 0, // OP_THUMB_SWI,
+ kCatAlu, // OP_THUMB_TST,
+
+ 0, // OP_END
+};
+
+const char *opcode_names[] = {
+ "invalid",
+ "undefined",
+ "adc",
+ "add",
+ "and",
+ "b",
+ "bl",
+ "bic",
+ "bkpt",
+ "blx",
+ "bx",
+ "cdp",
+ "clz",
+ "cmn",
+ "cmp",
+ "eor",
+ "ldc",
+ "ldm",
+ "ldr",
+ "ldrb",
+ "ldrbt",
+ "ldrh",
+ "ldrsb",
+ "ldrsh",
+ "ldrt",
+ "mcr",
+ "mla",
+ "mov",
+ "mrc",
+ "mrs",
+ "msr",
+ "mul",
+ "mvn",
+ "orr",
+ "pld",
+ "rsb",
+ "rsc",
+ "sbc",
+ "smlal",
+ "smull",
+ "stc",
+ "stm",
+ "str",
+ "strb",
+ "strbt",
+ "strh",
+ "strt",
+ "sub",
+ "swi",
+ "swp",
+ "swpb",
+ "teq",
+ "tst",
+ "umlal",
+ "umull",
+
+ "undefined",
+ "adc",
+ "add",
+ "and",
+ "asr",
+ "b",
+ "bic",
+ "bkpt",
+ "bl",
+ "blx",
+ "bx",
+ "cmn",
+ "cmp",
+ "eor",
+ "ldmia",
+ "ldr",
+ "ldrb",
+ "ldrh",
+ "ldrsb",
+ "ldrsh",
+ "lsl",
+ "lsr",
+ "mov",
+ "mul",
+ "mvn",
+ "neg",
+ "orr",
+ "pop",
+ "push",
+ "ror",
+ "sbc",
+ "stmia",
+ "str",
+ "strb",
+ "strh",
+ "sub",
+ "swi",
+ "tst",
+
+ NULL
+};
diff --git a/emulator/qtools/opcode.h b/emulator/qtools/opcode.h
new file mode 100644
index 0000000..c8b67a6
--- /dev/null
+++ b/emulator/qtools/opcode.h
@@ -0,0 +1,166 @@
+// Copyright 2006 The Android Open Source Project
+
+#ifndef OPCODE_H
+#define OPCODE_H
+
+#include <inttypes.h>
+
+// Note: this list of opcodes must match the list used to initialize
+// the opflags[] array in opcode.cpp.
+enum Opcode {
+ OP_INVALID,
+ OP_UNDEFINED,
+ OP_ADC,
+ OP_ADD,
+ OP_AND,
+ OP_B,
+ OP_BL,
+ OP_BIC,
+ OP_BKPT,
+ OP_BLX,
+ OP_BX,
+ OP_CDP,
+ OP_CLZ,
+ OP_CMN,
+ OP_CMP,
+ OP_EOR,
+ OP_LDC,
+ OP_LDM,
+ OP_LDR,
+ OP_LDRB,
+ OP_LDRBT,
+ OP_LDRH,
+ OP_LDRSB,
+ OP_LDRSH,
+ OP_LDRT,
+ OP_MCR,
+ OP_MLA,
+ OP_MOV,
+ OP_MRC,
+ OP_MRS,
+ OP_MSR,
+ OP_MUL,
+ OP_MVN,
+ OP_ORR,
+ OP_PLD,
+ OP_RSB,
+ OP_RSC,
+ OP_SBC,
+ OP_SMLAL,
+ OP_SMULL,
+ OP_STC,
+ OP_STM,
+ OP_STR,
+ OP_STRB,
+ OP_STRBT,
+ OP_STRH,
+ OP_STRT,
+ OP_SUB,
+ OP_SWI,
+ OP_SWP,
+ OP_SWPB,
+ OP_TEQ,
+ OP_TST,
+ OP_UMLAL,
+ OP_UMULL,
+
+ // Define thumb opcodes
+ OP_THUMB_UNDEFINED,
+ OP_THUMB_ADC,
+ OP_THUMB_ADD,
+ OP_THUMB_AND,
+ OP_THUMB_ASR,
+ OP_THUMB_B,
+ OP_THUMB_BIC,
+ OP_THUMB_BKPT,
+ OP_THUMB_BL,
+ OP_THUMB_BLX,
+ OP_THUMB_BX,
+ OP_THUMB_CMN,
+ OP_THUMB_CMP,
+ OP_THUMB_EOR,
+ OP_THUMB_LDMIA,
+ OP_THUMB_LDR,
+ OP_THUMB_LDRB,
+ OP_THUMB_LDRH,
+ OP_THUMB_LDRSB,
+ OP_THUMB_LDRSH,
+ OP_THUMB_LSL,
+ OP_THUMB_LSR,
+ OP_THUMB_MOV,
+ OP_THUMB_MUL,
+ OP_THUMB_MVN,
+ OP_THUMB_NEG,
+ OP_THUMB_ORR,
+ OP_THUMB_POP,
+ OP_THUMB_PUSH,
+ OP_THUMB_ROR,
+ OP_THUMB_SBC,
+ OP_THUMB_STMIA,
+ OP_THUMB_STR,
+ OP_THUMB_STRB,
+ OP_THUMB_STRH,
+ OP_THUMB_SUB,
+ OP_THUMB_SWI,
+ OP_THUMB_TST,
+
+ OP_END // must be last
+};
+
+extern uint32_t opcode_flags[];
+extern const char *opcode_names[];
+
+// Define bit flags for the opcode categories
+static const uint32_t kCatByte = 0x0001;
+static const uint32_t kCatHalf = 0x0002;
+static const uint32_t kCatWord = 0x0004;
+static const uint32_t kCatLong = 0x0008;
+static const uint32_t kCatNumBytes = (kCatByte | kCatHalf | kCatWord | kCatLong);
+static const uint32_t kCatMultiple = 0x0010;
+static const uint32_t kCatSigned = 0x0020;
+static const uint32_t kCatLoad = 0x0040;
+static const uint32_t kCatStore = 0x0080;
+static const uint32_t kCatMemoryRef = (kCatLoad | kCatStore);
+static const uint32_t kCatAlu = 0x0100;
+static const uint32_t kCatBranch = 0x0200;
+static const uint32_t kCatBranchLink = 0x0400;
+static const uint32_t kCatBranchExch = 0x0800;
+static const uint32_t kCatCoproc = 0x1000;
+static const uint32_t kCatLoadMultiple = (kCatLoad | kCatMultiple);
+static const uint32_t kCatStoreMultiple = (kCatStore | kCatMultiple);
+
+inline bool isALU(Opcode op) { return (opcode_flags[op] & kCatAlu) != 0; }
+inline bool isBranch(Opcode op) { return (opcode_flags[op] & kCatBranch) != 0; }
+inline bool isBranchLink(Opcode op) {
+ return (opcode_flags[op] & kCatBranchLink) != 0;
+}
+inline bool isBranchExch(Opcode op) {
+ return (opcode_flags[op] & kCatBranchExch) != 0;
+}
+inline bool isLoad(Opcode op) { return (opcode_flags[op] & kCatLoad) != 0; }
+inline bool isLoadMultiple(Opcode op) {
+ return (opcode_flags[op] & kCatLoadMultiple) == kCatLoadMultiple;
+}
+inline bool isStoreMultiple(Opcode op) {
+ return (opcode_flags[op] & kCatStoreMultiple) == kCatStoreMultiple;
+}
+inline bool isStore(Opcode op) { return (opcode_flags[op] & kCatStore) != 0; }
+inline bool isSigned(Opcode op) { return (opcode_flags[op] & kCatSigned) != 0; }
+inline bool isMemoryRef(Opcode op) {
+ return (opcode_flags[op] & kCatMemoryRef) != 0;
+}
+inline int getAccessSize(Opcode op) { return opcode_flags[op] & kCatNumBytes; }
+inline bool isCoproc(Opcode op) { return (opcode_flags[op] & kCatCoproc) != 0; }
+inline int getNumAccesses(Opcode op, uint32_t binary) {
+ extern int num_one_bits[];
+ int num_accesses = 0;
+ if (opcode_flags[op] & kCatNumBytes)
+ num_accesses = 1;
+ else if (opcode_flags[op] & kCatMultiple) {
+ num_accesses = num_one_bits[(binary >> 8) & 0xff]
+ + num_one_bits[binary & 0xff];
+ }
+ return num_accesses;
+}
+
+#endif // OPCODE_H
diff --git a/emulator/qtools/parse_options-inl.h b/emulator/qtools/parse_options-inl.h
new file mode 100644
index 0000000..f218cc1
--- /dev/null
+++ b/emulator/qtools/parse_options-inl.h
@@ -0,0 +1,147 @@
+// Copyright 2006 The Android Open Source Project
+
+#ifndef PARSE_OPTIONS_INL_H
+#define PARSE_OPTIONS_INL_H
+
+// Define a typedef for TraceReaderType and include "parse_options.h"
+// before including this header file in a C++ source file.
+//
+// For example:
+//
+// struct symbol {
+// int elapsed;
+// };
+//
+// typedef TraceReader<symbol> TraceReaderType;
+
+
+typedef TraceReaderType::symbol_type symbol_type;
+typedef TraceReaderType::region_type region_type;
+typedef TraceReaderType::ProcessState ProcessState;
+
+symbol_type *kernel_sym;
+symbol_type *library_sym;
+
+// Returns true if the given event is included (or not excluded)
+// from the list of valid events specified by the user on the
+// command line.
+inline bool IsValidEvent(BBEvent *event, symbol_type *sym)
+{
+ if (include_some_pids && pid_include_vector.GetBit(event->pid) == 0)
+ return false;
+ if (exclude_some_pids && pid_exclude_vector.GetBit(event->pid))
+ return false;
+ if (include_some_procedures) {
+ if (sym == NULL || included_procedures.Find(sym->name) == 0)
+ return false;
+ }
+ if (exclude_some_procedures) {
+ if (sym == NULL || excluded_procedures.Find(sym->name))
+ return false;
+ }
+ return true;
+}
+
+inline symbol_type *GetSymbol(TraceReaderType *trace, int pid, uint32_t addr,
+ uint64_t time)
+{
+ symbol_type *sym = trace->LookupFunction(pid, addr, time);
+
+ if (lump_kernel && (sym->region->flags & region_type::kIsKernelRegion)) {
+ if (kernel_sym == NULL) {
+ kernel_sym = sym;
+ sym->name = ":kernel";
+ } else {
+ sym = kernel_sym;
+ }
+ }
+
+ if (lump_libraries && (sym->region->flags & region_type::kIsLibraryRegion)) {
+ if (library_sym == NULL) {
+ library_sym = sym;
+ sym->name = ":libs";
+ } else {
+ sym = library_sym;
+ }
+ }
+
+ return sym;
+}
+
+inline bool IsIncludedProcedure(symbol_type *sym)
+{
+ if (include_kernel_syms && (sym->region->flags & region_type::kIsKernelRegion))
+ return true;
+ if (include_library_syms && (sym->region->flags & region_type::kIsLibraryRegion))
+ return true;
+ return included_procedures.Find(sym->name);
+}
+
+inline bool IsExcludedProcedure(symbol_type *sym)
+{
+ if (exclude_kernel_syms && (sym->region->flags & region_type::kIsKernelRegion))
+ return true;
+ if (exclude_library_syms && (sym->region->flags & region_type::kIsLibraryRegion))
+ return true;
+ return excluded_procedures.Find(sym->name);
+}
+
+// Returns true on end-of-file.
+inline bool GetNextValidEvent(TraceReaderType *trace,
+ BBEvent *event,
+ BBEvent *first_ignored_event,
+ symbol_type **sym_ptr)
+{
+ symbol_type *sym = NULL;
+ first_ignored_event->time = 0;
+ if (trace->ReadBB(event))
+ return true;
+ bool recheck = true;
+ while (recheck) {
+ recheck = false;
+ if (include_some_pids) {
+ while (pid_include_vector.GetBit(event->pid) == 0) {
+ if (first_ignored_event->time == 0)
+ *first_ignored_event = *event;
+ if (trace->ReadBB(event))
+ return true;
+ }
+ } else if (exclude_some_pids) {
+ while (pid_exclude_vector.GetBit(event->pid)) {
+ if (first_ignored_event->time == 0)
+ *first_ignored_event = *event;
+ if (trace->ReadBB(event))
+ return true;
+ }
+ }
+
+ if (include_some_procedures) {
+ sym = GetSymbol(trace, event->pid, event->bb_addr, event->time);
+ while (!IsIncludedProcedure(sym)) {
+ if (first_ignored_event->time == 0)
+ *first_ignored_event = *event;
+ if (trace->ReadBB(event))
+ return true;
+ recheck = true;
+ sym = GetSymbol(trace, event->pid, event->bb_addr, event->time);
+ }
+ } else if (exclude_some_procedures) {
+ sym = GetSymbol(trace, event->pid, event->bb_addr, event->time);
+ while (IsExcludedProcedure(sym)) {
+ if (first_ignored_event->time == 0)
+ *first_ignored_event = *event;
+ if (trace->ReadBB(event))
+ return true;
+ recheck = true;
+ sym = GetSymbol(trace, event->pid, event->bb_addr, event->time);
+ }
+ }
+ }
+ if (sym == NULL)
+ sym = GetSymbol(trace, event->pid, event->bb_addr, event->time);
+
+ *sym_ptr = sym;
+ return false;
+}
+
+#endif // PARSE_OPTIONS_INL_H
diff --git a/emulator/qtools/parse_options.cpp b/emulator/qtools/parse_options.cpp
new file mode 100644
index 0000000..395e9a1
--- /dev/null
+++ b/emulator/qtools/parse_options.cpp
@@ -0,0 +1,119 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <ctype.h>
+#include "bitvector.h"
+#include "parse_options.h"
+#include "hash_table.h"
+
+const char *root = "";
+bool lump_kernel;
+bool lump_libraries;
+Bitvector pid_include_vector(32768);
+Bitvector pid_exclude_vector(32768);
+bool include_some_pids;
+bool exclude_some_pids;
+
+HashTable<int> excluded_procedures(2000);
+HashTable<int> included_procedures(2000);
+bool exclude_some_procedures;
+bool include_some_procedures;
+
+bool exclude_kernel_syms;
+bool exclude_library_syms;
+bool include_kernel_syms;
+bool include_library_syms;
+bool demangle = true;
+
+static const char *OptionsUsageStr =
+ " -e :kernel exclude all kernel symbols\n"
+ " -e :libs exclude all library symbols\n"
+ " -e <func> exclude function <func>\n"
+ " -e <pid> exclude process <pid>\n"
+ " -i :kernel include all kernel symbols\n"
+ " -i :libs include all library symbols\n"
+ " -i <func> include function <func>\n"
+ " -i <pid> include process <pid>\n"
+ " -l :kernel lump all the kernel symbols together\n"
+ " -l :libs lump all the library symbols together\n"
+ " -m do not demangle C++ symbols (m for 'mangle')\n"
+ " -r <root> use <root> as the path for finding ELF executables\n"
+ ;
+
+void OptionsUsage()
+{
+ fprintf(stderr, "%s", OptionsUsageStr);
+}
+
+void ParseOptions(int argc, char **argv)
+{
+ bool err = false;
+ while (!err) {
+ int opt = getopt(argc, argv, "+e:i:l:mr:");
+ if (opt == -1)
+ break;
+ switch (opt) {
+ case 'e':
+ if (*optarg == ':') {
+ if (strcmp(optarg, ":kernel") == 0)
+ exclude_kernel_syms = true;
+ else if (strcmp(optarg, ":libs") == 0)
+ exclude_library_syms = true;
+ else
+ err = true;
+ excluded_procedures.Update(optarg, 1);
+ exclude_some_procedures = true;
+ } else if (isdigit(*optarg)) {
+ int bitnum = atoi(optarg);
+ pid_exclude_vector.SetBit(bitnum);
+ exclude_some_pids = true;
+ } else {
+ excluded_procedures.Update(optarg, 1);
+ exclude_some_procedures = true;
+ }
+ break;
+ case 'i':
+ if (*optarg == ':') {
+ if (strcmp(optarg, ":kernel") == 0)
+ include_kernel_syms = true;
+ else if (strcmp(optarg, ":libs") == 0)
+ include_library_syms = true;
+ else
+ err = true;
+ included_procedures.Update(optarg, 1);
+ include_some_procedures = true;
+ } else if (isdigit(*optarg)) {
+ int bitnum = atoi(optarg);
+ pid_include_vector.SetBit(bitnum);
+ include_some_pids = true;
+ } else {
+ included_procedures.Update(optarg, 1);
+ include_some_procedures = true;
+ }
+ break;
+ case 'l':
+ if (strcmp(optarg, ":kernel") == 0)
+ lump_kernel = true;
+ else if (strcmp(optarg, ":libs") == 0)
+ lump_libraries = true;
+ else
+ err = true;
+ break;
+ case 'm':
+ demangle = false;
+ break;
+ case 'r':
+ root = optarg;
+ break;
+ default:
+ err = true;
+ break;
+ }
+ }
+
+ if (err) {
+ Usage(argv[0]);
+ exit(1);
+ }
+}
diff --git a/emulator/qtools/parse_options.h b/emulator/qtools/parse_options.h
new file mode 100644
index 0000000..aacbb9e
--- /dev/null
+++ b/emulator/qtools/parse_options.h
@@ -0,0 +1,32 @@
+// Copyright 2006 The Android Open Source Project
+
+#ifndef PARSE_OPTIONS_H
+#define PARSE_OPTIONS_H
+
+#include "bitvector.h"
+#include "hash_table.h"
+
+extern const char *root;
+extern bool lump_kernel;
+extern bool lump_libraries;
+extern Bitvector pid_include_vector;
+extern Bitvector pid_exclude_vector;
+extern bool include_some_pids;
+extern bool exclude_some_pids;
+
+extern HashTable<int> excluded_procedures;
+extern HashTable<int> included_procedures;
+extern bool exclude_some_procedures;
+extern bool include_some_procedures;
+
+extern bool exclude_kernel_syms;
+extern bool exclude_library_syms;
+extern bool include_kernel_syms;
+extern bool include_library_syms;
+extern bool demangle;
+
+extern void Usage(const char *program);
+extern void ParseOptions(int argc, char **argv);
+extern void OptionsUsage();
+
+#endif // PARSE_OPTIONS_H
diff --git a/emulator/qtools/post_trace.cpp b/emulator/qtools/post_trace.cpp
new file mode 100644
index 0000000..99525fb
--- /dev/null
+++ b/emulator/qtools/post_trace.cpp
@@ -0,0 +1,151 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <inttypes.h>
+#include "trace_reader.h"
+
+typedef struct MyStaticRec {
+ StaticRec bb;
+ uint32_t *insns;
+} MyStaticRec;
+
+const int kNumPids = 32768;
+char usedPids[kNumPids];
+
+int main(int argc, char **argv) {
+ uint32_t insns[kMaxInsnPerBB];
+
+ if (argc != 2) {
+ fprintf(stderr, "Usage: %s trace_file\n", argv[0]);
+ exit(1);
+ }
+
+ char *trace_filename = argv[1];
+ TraceReaderBase *trace = new TraceReaderBase;
+ trace->SetPostProcessing(true);
+ trace->Open(trace_filename);
+
+ // Count the number of static basic blocks and instructions.
+ uint64_t num_static_bb = 0;
+ uint64_t num_static_insn = 0;
+ while (1) {
+ StaticRec static_rec;
+
+ if (trace->ReadStatic(&static_rec))
+ break;
+ if (static_rec.bb_num != num_static_bb) {
+ fprintf(stderr,
+ "Error: basic block numbers out of order; expected %lld, got %lld\n",
+ num_static_bb, static_rec.bb_num);
+ exit(1);
+ }
+ num_static_bb += 1;
+ num_static_insn += static_rec.num_insns;
+ trace->ReadStaticInsns(static_rec.num_insns, insns);
+ }
+ trace->Close();
+
+ // Allocate space for all of the static blocks
+ MyStaticRec *blocks = new MyStaticRec[num_static_bb];
+
+ // Read the static blocks again and save pointers to them
+ trace->Open(trace_filename);
+ for (uint32_t ii = 0; ii < num_static_bb; ++ii) {
+ trace->ReadStatic(&blocks[ii].bb);
+ uint32_t num_insns = blocks[ii].bb.num_insns;
+ if (num_insns > 0) {
+ blocks[ii].insns = new uint32_t[num_insns];
+ trace->ReadStaticInsns(num_insns, blocks[ii].insns);
+ }
+ }
+
+ // Check the last basic block. If it contains a special undefined
+ // instruction, then truncate the basic block at that point.
+ uint32_t num_insns = blocks[num_static_bb - 1].bb.num_insns;
+ uint32_t *insn_ptr = blocks[num_static_bb - 1].insns;
+ for (uint32_t ii = 0; ii < num_insns; ++ii, ++insn_ptr) {
+ if (*insn_ptr == 0xe6c00110) {
+ uint32_t actual_num_insns = ii + 1;
+ blocks[num_static_bb - 1].bb.num_insns = actual_num_insns;
+ num_static_insn -= (num_insns - actual_num_insns);
+
+ // Write the changes back to the trace file
+ trace->TruncateLastBlock(actual_num_insns);
+ break;
+ }
+ }
+ TraceHeader *header = trace->GetHeader();
+ strcpy(header->ident, TRACE_IDENT);
+ header->num_static_bb = num_static_bb;
+ header->num_dynamic_bb = 0;
+ header->num_static_insn = num_static_insn;
+ header->num_dynamic_insn = 0;
+ trace->WriteHeader(header);
+
+ // Reopen the trace file in order to force the trace manager to reread
+ // the static blocks now that we have written that information to the
+ // header.
+ trace->Close();
+ trace->Open(trace_filename);
+
+ // Count the number of dynamic executions of basic blocks and instructions.
+ // Also keep track of which process ids are used.
+ uint64_t num_dynamic_bb = 0;
+ uint64_t num_dynamic_insn = 0;
+ while (1) {
+ BBEvent event;
+
+ if (trace->ReadBB(&event))
+ break;
+ if (event.bb_num >= num_static_bb) {
+ fprintf(stderr,
+ "Error: basic block number (%lld) too large (num blocks: %lld)\n",
+ event.bb_num, num_static_bb);
+ exit(1);
+ }
+ usedPids[event.pid] = 1;
+ num_dynamic_bb += 1;
+ num_dynamic_insn += event.num_insns;
+ }
+
+ // Count the number of process ids that are used and remember the first
+ // unused pid.
+ int numUsedPids = 0;
+ int unusedPid = -1;
+ for (int pid = 0; pid < kNumPids; pid++) {
+ if (usedPids[pid] == 1) {
+ numUsedPids += 1;
+ } else if (unusedPid == -1) {
+ unusedPid = pid;
+ }
+ }
+
+ // Rewrite the header with the dynamic counts
+ header->num_dynamic_bb = num_dynamic_bb;
+ header->num_dynamic_insn = num_dynamic_insn;
+ header->num_used_pids = numUsedPids;
+ header->first_unused_pid = unusedPid;
+ trace->WriteHeader(header);
+ trace->Close();
+
+ printf("Static basic blocks: %llu, Dynamic basic blocks: %llu\n",
+ num_static_bb, num_dynamic_bb);
+ printf("Static instructions: %llu, Dynamic instructions: %llu\n",
+ num_static_insn, num_dynamic_insn);
+
+ double elapsed_secs = header->elapsed_usecs / 1000000.0;
+ double insn_per_sec = 0;
+ if (elapsed_secs != 0)
+ insn_per_sec = num_dynamic_insn / elapsed_secs;
+ char *suffix = "";
+ if (insn_per_sec >= 1000000) {
+ insn_per_sec /= 1000000.0;
+ suffix = "M";
+ } else if (insn_per_sec > 1000) {
+ insn_per_sec /= 1000.0;
+ suffix = "K";
+ }
+ printf("Elapsed seconds: %.2f, simulated instructions/sec: %.1f%s\n",
+ elapsed_secs, insn_per_sec, suffix);
+ return 0;
+}
diff --git a/emulator/qtools/profile_pid.cpp b/emulator/qtools/profile_pid.cpp
new file mode 100644
index 0000000..aa37847
--- /dev/null
+++ b/emulator/qtools/profile_pid.cpp
@@ -0,0 +1,93 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <string.h>
+#include "trace_reader.h"
+#include "parse_options.h"
+
+typedef TraceReader<> TraceReaderType;
+
+#include "parse_options-inl.h"
+
+// This function is called from quicksort to compare the cpu time
+// of processes and sort into decreasing order.
+int cmp_dec_cpu_time(const void *a, const void *b) {
+ ProcessState *proc1, *proc2;
+
+ proc1 = (ProcessState*)a;
+ proc2 = (ProcessState*)b;
+ if (proc1 == NULL) {
+ if (proc2 == NULL)
+ return 0;
+ return 1;
+ }
+ if (proc2 == NULL)
+ return -1;
+ if (proc1->cpu_time < proc2->cpu_time)
+ return 1;
+ if (proc1->cpu_time > proc2->cpu_time)
+ return -1;
+ // If the cpu_time times are the same, then sort into increasing
+ // order of pid.
+ return proc1->pid - proc2->pid;
+}
+
+void Usage(const char *program)
+{
+ fprintf(stderr, "Usage: %s [options] trace_file\n", program);
+ OptionsUsage();
+}
+
+int main(int argc, char **argv) {
+ // Parse the options
+ ParseOptions(argc, argv);
+ if (argc - optind != 1) {
+ Usage(argv[0]);
+ exit(1);
+ }
+
+ char *trace_filename = argv[optind];
+ TraceReader<> *trace = new TraceReader<>;
+ trace->Open(trace_filename);
+ trace->SetRoot(root);
+
+ while (1) {
+ BBEvent event, ignored;
+ symbol_type *dummy_sym;
+
+ if (GetNextValidEvent(trace, &event, &ignored, &dummy_sym))
+ break;
+ }
+
+ int num_procs;
+ ProcessState *processes = trace->GetProcesses(&num_procs);
+ qsort(processes, num_procs, sizeof(ProcessState), cmp_dec_cpu_time);
+
+ uint64_t total_time = 0;
+ for (int ii = 0; ii < num_procs; ++ii) {
+ total_time += processes[ii].cpu_time;
+ }
+
+ uint64_t sum_time = 0;
+ printf(" pid parent cpu_time %% %% flags argv\n");
+ ProcessState *pstate = &processes[0];
+ for (int ii = 0; ii < num_procs; ++ii, ++pstate) {
+ sum_time += pstate->cpu_time;
+ double per = 100.0 * pstate->cpu_time / total_time;
+ double sum_per = 100.0 * sum_time / total_time;
+ char *print_flags = "";
+ if ((pstate->flags & ProcessState::kCalledExec) == 0)
+ print_flags = "T";
+ if (pstate->name == NULL)
+ pstate->name = "";
+ printf("%5d %5d %10llu %6.2f %6.2f %5s %s",
+ pstate->pid, pstate->parent_pid, pstate->cpu_time,
+ per, sum_per, print_flags, pstate->name);
+ for (int ii = 1; ii < pstate->argc; ++ii) {
+ printf(" %s", pstate->argv[ii]);
+ }
+ printf("\n");
+ }
+ return 0;
+}
diff --git a/emulator/qtools/profile_trace.cpp b/emulator/qtools/profile_trace.cpp
new file mode 100644
index 0000000..9d14a03
--- /dev/null
+++ b/emulator/qtools/profile_trace.cpp
@@ -0,0 +1,131 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <inttypes.h>
+#include "trace_reader.h"
+#include "parse_options.h"
+
+const int kMillion = 1000000;
+const int kMHz = 200 * kMillion;
+
+struct symbol {
+ int count; // number of times a function is executed
+ uint64_t elapsed; // elapsed time for this function
+};
+
+typedef TraceReader<symbol> TraceReaderType;
+
+#include "parse_options-inl.h"
+
+static const uint32_t kOffsetThreshold = 0x100000;
+
+// This comparison function is called from qsort() to sort
+// symbols into decreasing elapsed time.
+int cmp_sym_elapsed(const void *a, const void *b) {
+ const symbol_type *syma, *symb;
+ uint64_t elapsed1, elapsed2;
+
+ syma = static_cast<symbol_type const *>(a);
+ symb = static_cast<symbol_type const *>(b);
+ elapsed1 = syma->elapsed;
+ elapsed2 = symb->elapsed;
+ if (elapsed1 < elapsed2)
+ return 1;
+ if (elapsed1 == elapsed2)
+ return strcmp(syma->name, symb->name);
+ return -1;
+}
+
+void Usage(const char *program)
+{
+ fprintf(stderr, "Usage: %s [options] trace_file elf_file\n", program);
+ OptionsUsage();
+}
+
+int main(int argc, char **argv)
+{
+ ParseOptions(argc, argv);
+ if (argc - optind != 2) {
+ Usage(argv[0]);
+ exit(1);
+ }
+
+ char *trace_filename = argv[optind++];
+ char *elf_file = argv[optind++];
+ TraceReader<symbol> *trace = new TraceReader<symbol>;
+ trace->Open(trace_filename);
+ trace->SetDemangle(demangle);
+ trace->ReadKernelSymbols(elf_file);
+ trace->SetRoot(root);
+
+ symbol_type dummy;
+ dummy.count = 0;
+ dummy.elapsed = 0;
+ symbol_type *prev_sym = &dummy;
+ uint64_t prev_bb_time = 0;
+ while (1) {
+ symbol_type *sym;
+ BBEvent event;
+ BBEvent first_ignored_event;
+
+ bool eof = GetNextValidEvent(trace, &event, &first_ignored_event, &sym);
+
+ // Assign the elapsed time to the previous function symbol
+ uint64_t elapsed = 0;
+ if (first_ignored_event.time != 0)
+ elapsed = first_ignored_event.time - prev_bb_time;
+ else if (!eof)
+ elapsed = event.time - prev_bb_time;
+ prev_sym->elapsed += elapsed;
+
+ if (eof)
+ break;
+
+ prev_bb_time = event.time;
+ sym->count += 1;
+ prev_sym = sym;
+#if 0
+ printf("t%lld bb_num: %d, bb_addr: 0x%x func: %s, addr: 0x%x, count: %d\n",
+ bb_time, bb_num, bb_addr, sym->name, sym->addr, sym->count);
+#endif
+ }
+
+ int nsyms;
+ symbol_type *syms = trace->GetSymbols(&nsyms);
+
+ // Sort the symbols into decreasing order of elapsed time
+ qsort(syms, nsyms, sizeof(symbol_type), cmp_sym_elapsed);
+
+ // Add up all the cycles
+ uint64_t total = 0;
+ symbol_type *sym = syms;
+ for (int ii = 0; ii < nsyms; ++ii, ++sym) {
+ total += sym->elapsed;
+ }
+
+ double secs = 1.0 * total / kMHz;
+ printf("Total seconds: %.2f, total cycles: %lld, MHz: %d\n\n",
+ secs, total, kMHz / kMillion);
+
+ uint64_t sum = 0;
+ printf("Elapsed secs Elapsed cyc %% %% Function\n");
+ sym = syms;
+ for (int ii = 0; ii < nsyms; ++ii, ++sym) {
+ if (sym->elapsed == 0)
+ break;
+ sum += sym->elapsed;
+ double per = 100.0 * sym->elapsed / total;
+ double sum_per = 100.0 * sum / total;
+ double secs = 1.0 * sym->elapsed / kMHz;
+ char *ksym = " ";
+ if (sym->region->flags & region_type::kIsKernelRegion)
+ ksym = "k";
+ printf("%12.2f %11lld %6.2f %6.2f %s %s\n",
+ secs, sym->elapsed, per, sum_per, ksym, sym->name);
+ }
+ delete[] syms;
+ delete trace;
+
+ return 0;
+}
diff --git a/emulator/qtools/q2dm.cpp b/emulator/qtools/q2dm.cpp
new file mode 100644
index 0000000..7f987dc
--- /dev/null
+++ b/emulator/qtools/q2dm.cpp
@@ -0,0 +1,275 @@
+// Copyright 2006 The Android Open Source Project
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <assert.h>
+#include "trace_reader.h"
+#include "bitvector.h"
+#include "parse_options.h"
+#include "dmtrace.h"
+#include "armdis.h"
+
+struct symbol {
+ uint32_t id;
+};
+
+typedef TraceReader<symbol> TraceReaderType;
+
+#include "parse_options-inl.h"
+#include "callstack.h"
+
+DmTrace *dmtrace;
+
+class MyFrame : public StackFrame<symbol_type> {
+ public:
+ void push(int stackLevel, uint64_t time, CallStackBase *base);
+ void pop(int stackLevel, uint64_t time, CallStackBase *base);
+};
+
+typedef CallStack<MyFrame> CallStackType;
+
+static const int kNumStackFrames = 500;
+static const int kMaxThreads = (32 * 1024);
+uint64_t thread_time[kMaxThreads];
+
+class FunctionStack {
+ public:
+ FunctionStack() {
+ top = 0;
+ }
+ void push(symbol_type *sym) {
+ if (top >= kNumStackFrames)
+ return;
+ frames[top] = sym;
+ top += 1;
+ }
+
+ symbol_type* pop() {
+ if (top <= 0) {
+ return NULL;
+ }
+ top -= 1;
+ return frames[top];
+ }
+
+ void showStack() {
+ fprintf(stderr, "top %d\n", top);
+ for (int ii = 0; ii < top; ii++) {
+ fprintf(stderr, " %d: %s\n", ii, frames[ii]->name);
+ }
+ }
+
+ private:
+ int top;
+ symbol_type *frames[kNumStackFrames];
+};
+
+FunctionStack *dmtrace_stack[kMaxThreads];
+
+void MyFrame::push(int stackLevel, uint64_t time, CallStackBase *base)
+{
+ int pid = base->getId();
+ CallStackType *stack = (CallStackType *) base;
+
+#if 0
+ fprintf(stderr, "native push t %llu p %d s %d fid %d 0x%x %s\n",
+ stack->getGlobalTime(time), pid, stackLevel,
+ function->id, function->addr, function->name);
+#endif
+
+ FunctionStack *fstack = dmtrace_stack[pid];
+ if (fstack == NULL) {
+ fstack = new FunctionStack();
+ dmtrace_stack[pid] = fstack;
+ }
+
+ fstack->push(function);
+ thread_time[pid] = time;
+ dmtrace->addFunctionEntry(function->id, time, pid);
+}
+
+void MyFrame::pop(int stackLevel, uint64_t time, CallStackBase *base)
+{
+ int pid = base->getId();
+ CallStackType *stack = (CallStackType *) base;
+
+#if 0
+ fprintf(stderr, "native pop t %llu p %d s %d fid %d 0x%x %s\n",
+ stack->getGlobalTime(time), pid, stackLevel,
+ function->id, function->addr, function->name);
+#endif
+
+ FunctionStack *fstack = dmtrace_stack[pid];
+ if (fstack == NULL) {
+ fstack = new FunctionStack();
+ dmtrace_stack[pid] = fstack;
+ }
+
+ symbol_type *sym = fstack->pop();
+ if (sym != NULL && sym != function) {
+ fprintf(stderr, "Error: q2dm function mismatch at time %llu pid %d sym %s\n",
+ stack->getGlobalTime(time), pid, sym->name);
+ fstack->showStack();
+ exit(1);
+ }
+
+ thread_time[pid] = time;
+ dmtrace->addFunctionExit(function->id, time, pid);
+}
+
+uint32_t nextFunctionId = 4;
+CallStackType *stacks[kMaxThreads];
+
+void Usage(const char *program)
+{
+ fprintf(stderr, "Usage: %s [options] trace_name elf_file dmtrace_name\n",
+ program);
+ OptionsUsage();
+}
+
+int main(int argc, char **argv)
+{
+ bool useKernelStack = true;
+
+ ParseOptions(argc, argv);
+ if (argc - optind != 3) {
+ Usage(argv[0]);
+ exit(1);
+ }
+
+ char *qemu_trace_file = argv[optind++];
+ char *elf_file = argv[optind++];
+ char *dmtrace_file = argv[optind++];
+ TraceReaderType *trace = new TraceReaderType;
+ trace->Open(qemu_trace_file);
+ trace->SetDemangle(demangle);
+ trace->ReadKernelSymbols(elf_file);
+ trace->SetRoot(root);
+ TraceHeader *qheader = trace->GetHeader();
+ uint64_t startTime = qheader->start_sec;
+ startTime = (startTime << 32) | qheader->start_usec;
+ int kernelPid = qheader->first_unused_pid;
+
+ dmtrace = new DmTrace;
+ dmtrace->open(dmtrace_file, startTime);
+
+ bool inKernel = false;
+ CallStackType *kernelStack = NULL;
+ if (useKernelStack) {
+ // Create a fake kernel thread stack where we will put all the kernel
+ // code.
+ kernelStack = new CallStackType(kernelPid, kNumStackFrames, trace);
+ dmtrace->addThread(kernelPid, "(kernel)");
+ }
+
+ CallStackType *prevStack = NULL;
+ BBEvent event;
+ while (1) {
+ BBEvent ignored;
+ symbol_type *function;
+
+ if (GetNextValidEvent(trace, &event, &ignored, &function))
+ break;
+ if (event.bb_num == 0)
+ break;
+#if 0
+ fprintf(stderr, "event t %llu p %d %s\n",
+ event.time, event.pid, function->name);
+#endif
+
+ CallStackType *pStack;
+ if (useKernelStack) {
+ uint32_t flags = function->region->flags;
+ uint32_t region_mask = region_type::kIsKernelRegion
+ | region_type::kIsUserMappedRegion;
+ if ((flags & region_mask) == region_type::kIsKernelRegion) {
+ // Use the kernel stack
+ pStack = kernelStack;
+ inKernel = true;
+ } else {
+ // If we were just in the kernel then pop off all of the
+ // stack frames for the kernel thread.
+ if (inKernel == true) {
+ inKernel = false;
+ kernelStack->popAll(event.time);
+ }
+
+ // Get the stack for the current thread
+ pStack = stacks[event.pid];
+ }
+ } else {
+ // Get the stack for the current thread
+ pStack = stacks[event.pid];
+ }
+
+ // If the stack does not exist, then allocate a new one.
+ if (pStack == NULL) {
+ pStack = new CallStackType(event.pid, kNumStackFrames, trace);
+ stacks[event.pid] = pStack;
+ char *name = trace->GetProcessName(event.pid);
+ dmtrace->addThread(event.pid, name);
+ }
+
+ if (prevStack != pStack) {
+ pStack->threadStart(event.time);
+ if (prevStack)
+ prevStack->threadStop(event.time);
+ }
+ prevStack = pStack;
+
+ // If we have never seen this function before, then add it to the
+ // list of known functions.
+ if (function->id == 0) {
+ function->id = nextFunctionId;
+ nextFunctionId += 4;
+ uint32_t flags = function->region->flags;
+ const char *name = function->name;
+ if (flags & region_type::kIsKernelRegion) {
+ // To distinguish kernel function names from user library
+ // names, add a marker to the name.
+ int len = strlen(name) + strlen(" [kernel]") + 1;
+ char *kernelName = new char[len];
+ strcpy(kernelName, name);
+ strcat(kernelName, " [kernel]");
+ name = kernelName;
+ }
+ dmtrace->parseAndAddFunction(function->id, name);
+ }
+
+ // Update the stack
+ pStack->updateStack(&event, function);
+ }
+
+ if (prevStack == NULL) {
+ fprintf(stderr, "Error: no events in trace.\n");
+ exit(1);
+ }
+ prevStack->threadStop(event.time);
+ for (int ii = 0; ii < kMaxThreads; ++ii) {
+ if (stacks[ii]) {
+ stacks[ii]->threadStart(event.time);
+ stacks[ii]->popAll(event.time);
+ }
+ }
+ if (useKernelStack) {
+ kernelStack->popAll(event.time);
+ }
+
+ // Read the pid events to find the names of the processes
+ while (1) {
+ PidEvent pid_event;
+ if (trace->ReadPidEvent(&pid_event))
+ break;
+ if (pid_event.rec_type == kPidName) {
+ dmtrace->updateName(pid_event.pid, pid_event.path);
+ }
+ }
+
+
+ dmtrace->close();
+ delete dmtrace;
+ delete trace;
+ return 0;
+}
diff --git a/emulator/qtools/q2g.cpp b/emulator/qtools/q2g.cpp
new file mode 100644
index 0000000..6b2ae92
--- /dev/null
+++ b/emulator/qtools/q2g.cpp
@@ -0,0 +1,108 @@
+// Copyright 2006 The Android Open Source Project
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <assert.h>
+#include "trace_reader.h"
+#include "gtrace.h"
+#include "bitvector.h"
+#include "parse_options.h"
+
+struct symbol {
+ int filenum; // the file number (for gtrace)
+ int procnum; // the procedure number (for gtrace)
+};
+
+typedef TraceReader<symbol> TraceReaderType;
+
+#include "parse_options-inl.h"
+
+const int kMaxProcNum = 4095;
+int next_filenum = 1;
+int next_procnum = 1;
+
+void Usage(const char *program)
+{
+ fprintf(stderr, "Usage: %s [options] trace_file elf_file gtrace_file\n",
+ program);
+ OptionsUsage();
+}
+
+int main(int argc, char **argv)
+{
+ ParseOptions(argc, argv);
+ if (argc - optind != 3) {
+ Usage(argv[0]);
+ exit(1);
+ }
+
+ char *qemu_trace_file = argv[optind++];
+ char *elf_file = argv[optind++];
+ char *gtrace_file = argv[optind++];
+ TraceReader<symbol> *trace = new TraceReader<symbol>;
+ trace->Open(qemu_trace_file);
+ trace->ReadKernelSymbols(elf_file);
+ trace->SetRoot(root);
+ TraceHeader *qheader = trace->GetHeader();
+
+ // Get the first valid event to get the process id for the gtrace header.
+ BBEvent event;
+ BBEvent ignored;
+ symbol_type *sym;
+ if (GetNextValidEvent(trace, &event, &ignored, &sym))
+ return 0;
+
+ Gtrace *gtrace = new Gtrace;
+ gtrace->Open(gtrace_file, qheader->pdate, qheader->ptime);
+ gtrace->WriteFirstHeader(qheader->start_sec, event.pid);
+
+ symbol_type *prev_sym = NULL;
+ bool eof = false;
+ while (!eof) {
+ if (sym != prev_sym) {
+ // This procedure is different from the previous procedure.
+
+ // If we have never seen this symbol before, then add it to the
+ // list of known procedures.
+ if (sym->filenum == 0) {
+ sym->filenum = next_filenum;
+ sym->procnum = next_procnum;
+ gtrace->AddProcedure(sym->filenum, sym->procnum, sym->name);
+ next_procnum += 1;
+ if (next_procnum > kMaxProcNum) {
+ next_filenum += 1;
+ next_procnum = 1;
+ }
+ }
+
+ // If we haven't yet recorded the procedure exit for the previous
+ // procedure, then do it now.
+ if (prev_sym) {
+ gtrace->AddProcExit(prev_sym->filenum, prev_sym->procnum, event.time,
+ event.pid);
+ }
+
+ // If this is not the terminating record, then record a procedure
+ // entry.
+ if (event.bb_num != 0) {
+ gtrace->AddProcEntry(sym->filenum, sym->procnum, event.time, event.pid);
+ prev_sym = sym;
+ }
+ }
+
+ eof = GetNextValidEvent(trace, &event, &ignored, &sym);
+ if (ignored.time != 0 && prev_sym) {
+ // We read an event that we are ignoring.
+ // If we haven't already recorded a procedure exit, then do so.
+ gtrace->AddProcExit(prev_sym->filenum, prev_sym->procnum, ignored.time,
+ ignored.pid);
+ prev_sym = NULL;
+ }
+ }
+
+ delete gtrace;
+ delete trace;
+ return 0;
+}
diff --git a/emulator/qtools/read_addr.cpp b/emulator/qtools/read_addr.cpp
new file mode 100644
index 0000000..38fc62a
--- /dev/null
+++ b/emulator/qtools/read_addr.cpp
@@ -0,0 +1,29 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include "trace_reader.h"
+
+int main(int argc, char **argv) {
+ if (argc != 2) {
+ fprintf(stderr, "Usage: %s trace_file\n", argv[0]);
+ exit(1);
+ }
+
+ char *trace_filename = argv[1];
+ TraceReaderBase *trace = new TraceReaderBase;
+ trace->Open(trace_filename);
+
+ while (1) {
+ uint64_t time;
+ uint32_t addr;
+ int flags;
+
+ if (trace->ReadAddr(&time, &addr, &flags))
+ break;
+ char *op = "ld";
+ if (flags == 1)
+ op = "st";
+ printf("%lld 0x%08x %s\n", time, addr, op);
+ }
+ return 0;
+}
diff --git a/emulator/qtools/read_elf.cpp b/emulator/qtools/read_elf.cpp
new file mode 100644
index 0000000..1d1a74a
--- /dev/null
+++ b/emulator/qtools/read_elf.cpp
@@ -0,0 +1,210 @@
+/*************************************************************************
+ Copyright (C) 2002,2003,2004,2005 Wei Qin
+ See file COPYING for more information.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+*************************************************************************/
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include "read_elf.h"
+
+#define SwapHalf(a) (((a & 0x00ff) << 8) | ((a & 0xff00) >> 8))
+#define SwapWord(a) (((a & 0xff000000) >> 24) | ((a & 0x00ff0000) >> 8) | ((a & 0x0000ff00) << 8) | ((a & 0x000000ff) << 24))
+#define SwapAddr(a) SwapWord(a)
+#define SwapOff(a) SwapWord(a)
+#define SwapSection(a) SwapHalf(a)
+
+int LittleEndian()
+{
+ Elf32_Word a = 0x01020304;
+ return *(char *) &a == 0x04;
+}
+
+void SwapElfHeader(Elf32_Ehdr *hdr)
+{
+ hdr->e_type = SwapHalf(hdr->e_type);
+ hdr->e_machine = SwapHalf(hdr->e_machine);
+ hdr->e_version = SwapWord(hdr->e_version);
+ hdr->e_entry = SwapAddr(hdr->e_entry);
+ hdr->e_phoff = SwapOff(hdr->e_phoff);
+ hdr->e_shoff = SwapOff(hdr->e_shoff);
+ hdr->e_flags = SwapWord(hdr->e_flags);
+ hdr->e_ehsize = SwapHalf(hdr->e_ehsize);
+ hdr->e_phentsize = SwapHalf(hdr->e_phentsize);
+ hdr->e_phnum = SwapHalf(hdr->e_phnum);
+ hdr->e_shentsize = SwapHalf(hdr->e_shentsize);
+ hdr->e_shnum = SwapHalf(hdr->e_shnum);
+ hdr->e_shstrndx = SwapHalf(hdr->e_shstrndx);
+}
+
+void SwapSectionHeader(Elf32_Shdr *shdr)
+{
+ shdr->sh_name = SwapWord(shdr->sh_name);
+ shdr->sh_type = SwapWord(shdr->sh_type);
+ shdr->sh_flags = SwapWord(shdr->sh_flags);
+ shdr->sh_addr = SwapAddr(shdr->sh_addr);
+ shdr->sh_offset = SwapOff(shdr->sh_offset);
+ shdr->sh_size = SwapWord(shdr->sh_size);
+ shdr->sh_link = SwapWord(shdr->sh_link);
+ shdr->sh_info = SwapWord(shdr->sh_info);
+ shdr->sh_addralign = SwapWord(shdr->sh_addralign);
+ shdr->sh_entsize = SwapWord(shdr->sh_entsize);
+}
+
+void SwapElfSymbol(Elf32_Sym *sym)
+{
+ sym->st_name = SwapWord(sym->st_name);
+ sym->st_value = SwapAddr(sym->st_value);
+ sym->st_size = SwapWord(sym->st_size);
+ sym->st_shndx = SwapSection(sym->st_shndx);
+}
+
+void AdjustElfHeader(Elf32_Ehdr *hdr)
+{
+ switch(hdr->e_ident[EI_DATA])
+ {
+ case ELFDATA2LSB:
+ if (!LittleEndian())
+ SwapElfHeader(hdr);
+ break;
+ case ELFDATA2MSB:
+ if (LittleEndian())
+ SwapElfHeader(hdr);
+ break;
+ }
+}
+
+void AdjustSectionHeader(Elf32_Ehdr *hdr, Elf32_Shdr *shdr)
+{
+ switch(hdr->e_ident[EI_DATA])
+ {
+ case ELFDATA2LSB:
+ if (!LittleEndian())
+ SwapSectionHeader(shdr);
+ break;
+ case ELFDATA2MSB:
+ if (LittleEndian())
+ SwapSectionHeader(shdr);
+ break;
+ }
+}
+
+void AdjustElfSymbols(Elf32_Ehdr *hdr, Elf32_Sym *elf_symbols, int num_entries)
+{
+ if (hdr->e_ident[EI_DATA] == ELFDATA2LSB && LittleEndian())
+ return;
+ if (hdr->e_ident[EI_DATA] == ELFDATA2MSB && !LittleEndian())
+ return;
+ for (int ii = 0; ii < num_entries; ++ii) {
+ SwapElfSymbol(&elf_symbols[ii]);
+ }
+}
+
+Elf32_Ehdr *ReadElfHeader(FILE *fobj)
+{
+ Elf32_Ehdr *hdr = new Elf32_Ehdr;
+ int rval = fread(hdr, sizeof(Elf32_Ehdr), 1, fobj);
+ if (rval != 1) {
+ delete hdr;
+ return NULL;
+ }
+ if (hdr->e_ident[EI_MAG0] != 0x7f || hdr->e_ident[EI_MAG1] != 'E' ||
+ hdr->e_ident[EI_MAG2] != 'L' || hdr->e_ident[EI_MAG3] != 'F') {
+ delete hdr;
+ return NULL;
+ }
+ AdjustElfHeader(hdr);
+ return hdr;
+}
+
+Elf32_Shdr *ReadSectionHeaders(Elf32_Ehdr *hdr, FILE *f)
+{
+ int i;
+ unsigned long sz = hdr->e_shnum * hdr->e_shentsize;
+ assert(sizeof(Elf32_Shdr) == hdr->e_shentsize);
+ Elf32_Shdr *shdr = new Elf32_Shdr[hdr->e_shnum];
+
+ if (fseek(f, hdr->e_shoff, SEEK_SET) != 0)
+ {
+ delete[] shdr;
+ return NULL;
+ }
+ if (fread(shdr, sz, 1, f) != 1)
+ {
+ delete[] shdr;
+ return NULL;
+ }
+
+ for(i = 0; i < hdr->e_shnum; i++)
+ AdjustSectionHeader(hdr, shdr + i);
+
+ return shdr;
+}
+
+
+char *ReadStringTable(Elf32_Ehdr *hdr, Elf32_Shdr *shdr_table, FILE *f)
+{
+ Elf32_Shdr *shdr = shdr_table + hdr->e_shstrndx;
+ char *string_table;
+
+ string_table = new char[shdr->sh_size];
+ fseek(f, shdr->sh_offset, SEEK_SET);
+ fread(string_table, shdr->sh_size, 1, f);
+
+ return string_table;
+}
+
+int ReadSection(Elf32_Shdr *shdr, void *buffer, FILE *f)
+{
+ if (fseek(f, shdr->sh_offset, SEEK_SET) != 0)
+ return -1;
+ if (fread(buffer, shdr->sh_size, 1, f) != 1)
+ return -1;
+ return 0;
+}
+
+char *GetSymbolName(Elf32_Half index, char *string_table)
+{
+ return string_table + index;
+}
+
+Elf32_Shdr *FindSymbolTableSection(Elf32_Ehdr *hdr,
+ Elf32_Shdr *shdr,
+ char *string_table)
+{
+ for(int ii = 0; ii < hdr->e_shnum; ii++) {
+ if (shdr[ii].sh_type == SHT_SYMTAB &&
+ strcmp(GetSymbolName(shdr[ii].sh_name, string_table),
+ ".symtab") == 0)
+ {
+ return &shdr[ii];
+ }
+ }
+ return NULL;
+}
+
+Elf32_Shdr *FindSymbolStringTableSection(Elf32_Ehdr *hdr,
+ Elf32_Shdr *shdr,
+ char *string_table)
+{
+ for(int ii = 0; ii < hdr->e_shnum; ii++) {
+ if (shdr[ii].sh_type == SHT_STRTAB &&
+ strcmp(GetSymbolName(shdr[ii].sh_name, string_table),
+ ".strtab") == 0)
+ {
+ return &shdr[ii];
+ }
+ }
+ return NULL;
+}
diff --git a/emulator/qtools/read_elf.h b/emulator/qtools/read_elf.h
new file mode 100644
index 0000000..72266c3
--- /dev/null
+++ b/emulator/qtools/read_elf.h
@@ -0,0 +1,20 @@
+#ifndef READ_ELF_H
+#define READ_ELF_H
+
+#include <stdio.h>
+#include <elf.h>
+
+Elf32_Ehdr *ReadElfHeader(FILE *fobj);
+Elf32_Shdr *ReadSectionHeaders(Elf32_Ehdr *hdr, FILE *fobj);
+char *ReadStringTable(Elf32_Ehdr *hdr, Elf32_Shdr *shdr, FILE *fobj);
+Elf32_Shdr *FindSymbolTableSection(Elf32_Ehdr *hdr,
+ Elf32_Shdr *shdr,
+ char *string_table);
+Elf32_Shdr *FindSymbolStringTableSection(Elf32_Ehdr *hdr,
+ Elf32_Shdr *shdr,
+ char *string_table);
+int ReadSection(Elf32_Shdr *shdr, void *buffer, FILE *f);
+void AdjustElfSymbols(Elf32_Ehdr *hdr, Elf32_Sym *elf_symbols,
+ int num_entries);
+
+#endif /* READ_ELF_H */
diff --git a/emulator/qtools/read_method.cpp b/emulator/qtools/read_method.cpp
new file mode 100644
index 0000000..e48edad
--- /dev/null
+++ b/emulator/qtools/read_method.cpp
@@ -0,0 +1,52 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include "trace_reader.h"
+#include "parse_options.h"
+
+typedef TraceReader<> TraceReaderType;
+
+#include "parse_options-inl.h"
+
+void Usage(const char *program)
+{
+ fprintf(stderr, "Usage: %s [options] trace_name elf_file\n",
+ program);
+ OptionsUsage();
+}
+
+int main(int argc, char **argv) {
+ ParseOptions(argc, argv);
+ if (argc - optind != 2) {
+ Usage(argv[0]);
+ exit(1);
+ }
+
+ char *qemu_trace_file = argv[optind++];
+ char *elf_file = argv[optind++];
+ TraceReaderType *trace = new TraceReaderType;
+ trace->Open(qemu_trace_file);
+ trace->ReadKernelSymbols(elf_file);
+ trace->SetRoot(root);
+
+ while (1) {
+ MethodRec method_record;
+ symbol_type *sym;
+ TraceReaderType::ProcessState *proc;
+
+ if (trace->ReadMethodSymbol(&method_record, &sym, &proc))
+ break;
+ if (sym != NULL) {
+ printf("%lld p %d 0x%x %d %s\n",
+ method_record.time, proc->pid, method_record.addr,
+ method_record.flags, sym->name);
+ } else {
+ printf("%lld p %d 0x%x %d\n",
+ method_record.time, proc->pid, method_record.addr,
+ method_record.flags);
+ }
+ proc->DumpStack();
+ }
+ return 0;
+}
diff --git a/emulator/qtools/read_pid.cpp b/emulator/qtools/read_pid.cpp
new file mode 100644
index 0000000..a2d69d4
--- /dev/null
+++ b/emulator/qtools/read_pid.cpp
@@ -0,0 +1,71 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include "trace_reader.h"
+
+int main(int argc, char **argv) {
+ if (argc != 2) {
+ fprintf(stderr, "Usage: %s trace_file\n", argv[0]);
+ exit(1);
+ }
+
+ char *trace_filename = argv[1];
+ TraceReaderBase *trace = new TraceReaderBase;
+ trace->Open(trace_filename);
+
+ while (1) {
+ PidEvent event;
+ if (trace->ReadPidEvent(&event))
+ break;
+ switch (event.rec_type) {
+ case kPidFork:
+ printf("t%lld fork tgid %d pid %d\n", event.time, event.tgid, event.pid);
+ break;
+ case kPidClone:
+ printf("t%lld clone tgid %d pid %d\n", event.time, event.tgid, event.pid);
+ break;
+ case kPidSwitch:
+ printf("t%lld switch %d\n", event.time, event.pid);
+ break;
+ case kPidExit:
+ printf("t%lld exit %d\n", event.time, event.pid);
+ break;
+ case kPidMmap:
+ printf("t%lld mmap %08x - %08x, offset %08x '%s'\n",
+ event.time, event.vstart, event.vend, event.offset, event.path);
+ delete[] event.path;
+ break;
+ case kPidMunmap:
+ printf("t%lld munmap %08x - %08x\n",
+ event.time, event.vstart, event.vend);
+ break;
+ case kPidSymbolAdd:
+ printf("t%lld add sym %08x '%s'\n",
+ event.time, event.vstart, event.path);
+ delete[] event.path;
+ break;
+ case kPidSymbolRemove:
+ printf("t%lld remove %08x\n", event.time, event.vstart);
+ break;
+ case kPidExec:
+ printf("t%lld argc: %d\n", event.time, event.argc);
+ for (int ii = 0; ii < event.argc; ++ii) {
+ printf(" argv[%d]: %s\n", ii, event.argv[ii]);
+ delete[] event.argv[ii];
+ }
+ delete[] event.argv;
+ break;
+ case kPidKthreadName:
+ printf("t%lld kthread tgid %d pid %d %s\n",
+ event.time, event.tgid, event.pid, event.path);
+ delete[] event.path;
+ break;
+ case kPidName:
+ printf("t%lld name %d %s\n",
+ event.time, event.pid, event.path);
+ delete[] event.path;
+ break;
+ }
+ }
+ return 0;
+}
diff --git a/emulator/qtools/read_trace.cpp b/emulator/qtools/read_trace.cpp
new file mode 100644
index 0000000..fb4917c
--- /dev/null
+++ b/emulator/qtools/read_trace.cpp
@@ -0,0 +1,165 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <assert.h>
+#include "trace_reader.h"
+#include "armdis.h"
+#include "parse_options.h"
+
+typedef TraceReader<> TraceReaderType;
+
+#include "parse_options-inl.h"
+
+static const uint32_t kOffsetThreshold = 0x100000;
+static uint64_t startTime = 0;
+
+void Usage(const char *program)
+{
+ fprintf(stderr,
+ "Usage: %s [options] [-- -s start_time] trace_file elf_file\n",
+ program);
+ OptionsUsage();
+}
+
+
+bool localParseOptions(int argc, char **argv)
+{
+ bool err = false;
+ while (!err) {
+ int opt = getopt(argc, argv, "+s:");
+ if (opt == -1)
+ break;
+ switch (opt) {
+ case 's':
+ startTime = strtoull(optarg, NULL, 0);
+ break;
+ default:
+ err = true;
+ break;
+ }
+ }
+ return err;
+}
+
+int main(int argc, char **argv) {
+ // Parse the options
+ ParseOptions(argc, argv);
+ localParseOptions(argc, argv);
+ if (argc - optind != 2) {
+ Usage(argv[0]);
+ exit(1);
+ }
+
+ char *trace_filename = argv[optind++];
+ char *elf_file = argv[optind++];
+ TraceReader<> *trace = new TraceReader<>;
+ trace->Open(trace_filename);
+ trace->SetDemangle(demangle);
+ trace->ReadKernelSymbols(elf_file);
+ trace->SetRoot(root);
+
+ while (1) {
+ symbol_type *sym;
+ char buf[1024];
+ BBEvent event;
+ BBEvent ignored;
+
+ if (GetNextValidEvent(trace, &event, &ignored, &sym))
+ break;
+#if 0
+ fprintf(stderr, "t%llu bb %lld %d\n",
+ event.time, event.bb_num, event.num_insns);
+#endif
+
+ uint32_t *insns = event.insns;
+ uint32_t addr = event.bb_addr;
+ uint32_t offset = addr - sym->addr - sym->region->base_addr;
+ symbol_type *vm_sym = sym->vm_sym;
+ const char *vm_name = NULL;
+ if (vm_sym != NULL) {
+ vm_name = vm_sym->name;
+ offset = addr - vm_sym->addr - vm_sym->region->base_addr;
+ }
+#if 0
+ if (strcmp(sym->name, "(unknown)") == 0 || offset > kOffsetThreshold) {
+ ProcessState *process = trace->GetCurrentProcess();
+ ProcessState *manager = process->addr_manager;
+ for (int ii = 0; ii < manager->nregions; ++ii) {
+ printf(" %2d: %08x - %08x base: %08x offset: %u nsyms: %4d flags: 0x%x %s\n",
+ ii,
+ manager->regions[ii]->vstart,
+ manager->regions[ii]->vend,
+ manager->regions[ii]->base_addr,
+ manager->regions[ii]->file_offset,
+ manager->regions[ii]->nsymbols,
+ manager->regions[ii]->flags,
+ manager->regions[ii]->path);
+ int nsymbols = manager->regions[ii]->nsymbols;
+ for (int jj = 0; jj < 10 && jj < nsymbols; ++jj) {
+ printf(" %08x %s\n",
+ manager->regions[ii]->symbols[jj].addr,
+ manager->regions[ii]->symbols[jj].name);
+ }
+ }
+ }
+#endif
+#if 1
+ for (int ii = 0; ii < event.num_insns; ++ii) {
+ uint64_t sim_time = trace->ReadInsnTime(event.time);
+ if (sim_time < startTime)
+ continue;
+
+ uint32_t insn = insns[ii];
+ char *disasm;
+ int bytes;
+ if (vm_name != NULL) {
+ sprintf(buf, "%s+%02x: %s", vm_name, offset, sym->name);
+ } else {
+ sprintf(buf, "%s+%02x", sym->name, offset);
+ }
+
+ if (insn_is_thumb(insn)) {
+ bytes = 2;
+ insn = insn_unwrap_thumb(insn);
+
+ // thumb_pair is true if this is the first of a pair of
+ // thumb instructions (BL or BLX).
+ bool thumb_pair = ((insn & 0xf800) == 0xf000);
+
+ // Get the next thumb instruction (if any) because we may need
+ // it for the case where insn is BL or BLX.
+ uint32_t insn2 = 0;
+ if (thumb_pair && (ii + 1 < event.num_insns)) {
+ insn2 = insns[ii + 1];
+ insn2 = insn_unwrap_thumb(insn2);
+ bytes = 4;
+ ii += 1;
+ }
+ disasm = disasm_insn_thumb(addr, insn, insn2, NULL);
+ if (thumb_pair) {
+ printf("%llu p%-4d %08x %04x %04x %-30s %s\n",
+ sim_time, event.pid, addr, insn, insn2, buf, disasm);
+ } else {
+ printf("%llu p%-4d %08x %04x %-30s %s\n",
+ sim_time, event.pid, addr, insn, buf, disasm);
+ }
+ } else {
+ bytes = 4;
+ disasm = Arm::disasm(addr, insn, NULL);
+ printf("%llu p%-4d %08x %08x %-30s %s\n",
+ sim_time, event.pid, addr, insn, buf, disasm);
+ }
+ //printf("t%llu \t%08x\n", sim_time, addr);
+ addr += bytes;
+ offset += bytes;
+ }
+#endif
+#if 0
+ assert(offset < kOffsetThreshold);
+#endif
+ }
+
+ delete trace;
+ return 0;
+}
diff --git a/emulator/qtools/stack_dump.cpp b/emulator/qtools/stack_dump.cpp
new file mode 100644
index 0000000..b5014ef
--- /dev/null
+++ b/emulator/qtools/stack_dump.cpp
@@ -0,0 +1,105 @@
+// Copyright 2006 The Android Open Source Project
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <inttypes.h>
+#include <assert.h>
+#include "trace_reader.h"
+#include "bitvector.h"
+#include "parse_options.h"
+#include "armdis.h"
+
+typedef TraceReader<> TraceReaderType;
+
+#include "parse_options-inl.h"
+#include "callstack.h"
+
+class MyFrame : public StackFrame<symbol_type> {
+ public:
+ void push(int stackLevel, uint64_t time, CallStackBase *base);
+ void pop(int stackLevel, uint64_t time, CallStackBase *base);
+};
+
+typedef CallStack<MyFrame> CallStackType;
+
+void MyFrame::push(int stackLevel, uint64_t time, CallStackBase *base)
+{
+ printf("%llu en thr %d %3d", time, base->getId(), stackLevel);
+ for (int ii = 0; ii < stackLevel; ++ii)
+ printf(".");
+ printf(" 0x%08x %s\n", addr, function->name);
+}
+
+void MyFrame::pop(int stackLevel, uint64_t time, CallStackBase *base)
+{
+ printf("%llu x thr %d %3d", time, base->getId(), stackLevel);
+ for (int ii = 0; ii < stackLevel; ++ii)
+ printf(".");
+ printf(" 0x%08x %s\n", addr, function->name);
+}
+
+static const int kNumStackFrames = 500;
+static const int kMaxThreads = (32 * 1024);
+CallStackType *stacks[kMaxThreads];
+
+static uint64_t debugTime;
+
+void Usage(const char *program)
+{
+ fprintf(stderr, "Usage: %s [options] trace_name elf_file\n",
+ program);
+ OptionsUsage();
+}
+
+int main(int argc, char **argv)
+{
+ ParseOptions(argc, argv);
+ if (argc - optind != 2) {
+ Usage(argv[0]);
+ exit(1);
+ }
+
+ char *qemu_trace_file = argv[optind++];
+ char *elf_file = argv[optind++];
+ TraceReaderType *trace = new TraceReaderType;
+ trace->Open(qemu_trace_file);
+ trace->ReadKernelSymbols(elf_file);
+ trace->SetRoot(root);
+ TraceHeader *qheader = trace->GetHeader();
+ uint64_t startTime = qheader->start_sec;
+ startTime = (startTime << 32) | qheader->start_usec;
+
+ BBEvent event;
+ while (1) {
+ BBEvent ignored;
+ symbol_type *function;
+
+ if (GetNextValidEvent(trace, &event, &ignored, &function))
+ break;
+ if (event.bb_num == 0)
+ break;
+
+ // Get the stack for the current thread
+ CallStackType *pStack = stacks[event.pid];
+
+ // If the stack does not exist, then allocate a new one.
+ if (pStack == NULL) {
+ pStack = new CallStackType(event.pid, kNumStackFrames, trace);
+ stacks[event.pid] = pStack;
+ }
+ if (debugTime != 0 && event.time >= debugTime)
+ printf("debug time: %lld\n", debugTime);
+
+ // Update the stack
+ pStack->updateStack(&event, function);
+ }
+
+ for (int ii = 0; ii < kMaxThreads; ++ii) {
+ if (stacks[ii])
+ stacks[ii]->popAll(event.time);
+ }
+
+ delete trace;
+ return 0;
+}
diff --git a/emulator/qtools/tests/common_head.mk b/emulator/qtools/tests/common_head.mk
new file mode 100644
index 0000000..e8170e9
--- /dev/null
+++ b/emulator/qtools/tests/common_head.mk
@@ -0,0 +1,25 @@
+CC := arm-elf-gcc
+LD := arm-elf-ld
+AS := arm-elf-as
+OBJCOPY := arm-elf-objcopy
+OBJDUMP := arm-elf-objdump
+
+OPT := -g
+CFLAGS := $(OPT) -mcpu=arm9
+
+.SUFFIXES: .dis .bin .elf
+
+.c.elf:
+ $(CC) $(CFLAGS) -Xlinker --script ../tests.ld -o $@ $< -nostdlib
+
+.c.s:
+ $(CC) $(CFLAGS) -static -S $<
+
+.S.elf:
+ $(CC) $(CFLAGS) -Xlinker --script ../tests.ld -nostdlib -o $@ $<
+
+.elf.dis:
+ $(OBJDUMP) -adx $< > $@
+
+.elf.bin:
+ $(OBJCOPY) -O binary $< $@
diff --git a/emulator/qtools/tests/common_tail.mk b/emulator/qtools/tests/common_tail.mk
new file mode 100644
index 0000000..be1c141
--- /dev/null
+++ b/emulator/qtools/tests/common_tail.mk
@@ -0,0 +1,3 @@
+
+clean:
+ rm -f *.elf *.dis *.bin *.o *~ a.out
diff --git a/emulator/qtools/tests/gtrace/Makefile b/emulator/qtools/tests/gtrace/Makefile
new file mode 100644
index 0000000..1d2050c
--- /dev/null
+++ b/emulator/qtools/tests/gtrace/Makefile
@@ -0,0 +1,18 @@
+include ../common_head.mk
+
+P4ROOT=/work/android
+QEMU=$(P4ROOT)/device/tools/qemu/arm-softmmu/qemu-system-arm
+QTOOLS=$(P4ROOT)/device/tools/qtools
+
+all: test.elf test.bin test.dis
+
+trace: test.elf test.bin
+ $(QEMU) -QEMU -kernel test.bin -trace foo
+ $(QTOOLS)/post_trace foo
+ $(QTOOLS)/read_trace foo test.elf > t1
+
+gtrace: trace
+ $(QTOOLS)/q2g -r $(P4ROOT)/device/out/linux-arm-release/symbols foo test.elf foo.gtrace
+ gtracepost64 foo.gtrace > t2
+
+include ../common_tail.mk
diff --git a/emulator/qtools/tests/gtrace/test.c b/emulator/qtools/tests/gtrace/test.c
new file mode 100644
index 0000000..e56a0f1
--- /dev/null
+++ b/emulator/qtools/tests/gtrace/test.c
@@ -0,0 +1,201 @@
+#include "../macros.h"
+
+int foo1();
+int foo2();
+void bar();
+int child1();
+int child2();
+int child3();
+int child4();
+int child5();
+
+int global;
+
+void start()
+{
+ // Set the stack pointer
+ asm(" mov r13,#0x200000");
+ PRINT_STR("hello\n");
+ TRACE_INIT_NAME(701, "proc_foo");
+ TRACE_INIT_NAME(702, "proc_bar");
+ TRACE_SWITCH(701);
+ if (global++ > 0)
+ global++;
+ foo1();
+ TRACE_SWITCH(702);
+ if (global++ > 0)
+ global++;
+ bar();
+ TRACE_SWITCH(701);
+ if (global++ > 0)
+ global++;
+ foo2();
+ TRACE_SWITCH(703);
+ if (global++ > 0)
+ global++;
+ foo1();
+ TRACE_SWITCH(701);
+ if (global++ > 0)
+ global++;
+ foo1();
+
+ TRACE_SWITCH(704);
+ if (global++ > 0)
+ global++;
+ foo1();
+
+ TRACE_SWITCH(701);
+ if (global++ > 0)
+ global++;
+ foo1();
+
+ TRACE_SWITCH(705);
+ if (global++ > 0)
+ global++;
+ foo1();
+
+ TRACE_SWITCH(701);
+ if (global++ > 0)
+ global++;
+ foo1();
+
+ TRACE_SWITCH(706);
+ if (global++ > 0)
+ global++;
+ foo1();
+
+ TRACE_SWITCH(701);
+ if (global++ > 0)
+ global++;
+ foo1();
+
+ TRACE_SWITCH(707);
+ if (global++ > 0)
+ global++;
+ foo1();
+
+ TRACE_SWITCH(701);
+ if (global++ > 0)
+ global++;
+ foo1();
+
+ TRACE_SWITCH(708);
+ if (global++ > 0)
+ global++;
+ foo1();
+
+ TRACE_SWITCH(701);
+ if (global++ > 0)
+ global++;
+ foo1();
+
+ TRACE_SWITCH(709);
+ if (global++ > 0)
+ global++;
+ foo1();
+
+ TRACE_SWITCH(701);
+ if (global++ > 0)
+ global++;
+ foo1();
+
+ TRACE_SWITCH(710);
+ if (global++ > 0)
+ global++;
+ foo1();
+
+ TRACE_STOP_EMU();
+}
+
+int foo1()
+{
+ int a = 0;
+
+ int ii;
+ for (ii = 0; ii < 3; ++ii) {
+ a += child1();
+ a += child2();
+ a += child3();
+ }
+ return a;
+}
+
+int foo2()
+{
+ int a = 0;
+
+ int ii;
+ for (ii = 0; ii < 2; ++ii) {
+ a += child3();
+ a += child4();
+ a += child5();
+ }
+ return a;
+}
+
+#define kStride 64
+void bar()
+{
+ int a = 0;
+
+ static char mem[1000 * kStride];
+
+ int ii, jj;
+
+ for (ii = 0; ii < 4; ++ii) {
+ for (jj = 0; jj < 10; ++jj)
+ a += mem[jj * kStride];
+ foo1();
+ foo2();
+ }
+}
+
+int child1()
+{
+ int a = 0;
+
+ int ii;
+ for (ii = 0; ii < 2; ++ii)
+ a += ii;
+ return a;
+}
+
+int child2()
+{
+ int a = 0;
+
+ int ii;
+ for (ii = 0; ii < 4; ++ii)
+ a += ii;
+ return a;
+}
+
+int child3()
+{
+ int a = 0;
+
+ int ii;
+ for (ii = 0; ii < 6; ++ii)
+ a += ii;
+ return a;
+}
+
+int child4()
+{
+ int a = 0;
+
+ int ii;
+ for (ii = 0; ii < 8; ++ii)
+ a += ii;
+ return a;
+}
+
+int child5()
+{
+ int a = 0;
+
+ int ii;
+ for (ii = 0; ii < 10; ++ii)
+ a += ii;
+ return a;
+}
diff --git a/emulator/qtools/tests/macros.h b/emulator/qtools/tests/macros.h
new file mode 100644
index 0000000..066374b
--- /dev/null
+++ b/emulator/qtools/tests/macros.h
@@ -0,0 +1,93 @@
+#ifndef _TEST_TRACE_C_H_
+#define _TEST_TRACE_C_H_
+
+/* the base address of trace device */
+#define TRACE_DEV_BASE_ADDR 0x21000000
+
+/*the register addresses of the trace device */
+#define TRACE_DEV_REG_SWITCH 0
+#define TRACE_DEV_REG_FORK 1
+#define TRACE_DEV_REG_EXECVE_PID 2
+#define TRACE_DEV_REG_EXECVE_VMSTART 3
+#define TRACE_DEV_REG_EXECVE_VMEND 4
+#define TRACE_DEV_REG_EXECVE_OFFSET 5
+#define TRACE_DEV_REG_EXECVE_EXEPATH 6
+#define TRACE_DEV_REG_EXIT 7
+#define TRACE_DEV_REG_CMDLINE 8
+#define TRACE_DEV_REG_CMDLINE_LEN 9
+#define TRACE_DEV_REG_MMAP_EXEPATH 10
+#define TRACE_DEV_REG_INIT_PID 11
+#define TRACE_DEV_REG_INIT_NAME 12
+#define TRACE_DEV_REG_CLONE 13
+#define TRACE_DEV_REG_DYN_SYM 50
+#define TRACE_DEV_REG_DYN_SYM_ADDR 51
+#define TRACE_DEV_REG_PRINT_STR 60
+#define TRACE_DEV_REG_PRINT_NUM_DEC 61
+#define TRACE_DEV_REG_PRINT_NUM_HEX 62
+#define TRACE_DEV_REG_STOP_EMU 90
+#define TRACE_DEV_REG_ENABLE 100
+#define TRACE_DEV_REG_DISABLE 101
+
+/* write a word to a trace device register */
+#define DEV_WRITE_WORD(addr,value)\
+ (*(volatile unsigned long *)(TRACE_DEV_BASE_ADDR + ((addr) << 2)) = (value))
+
+/*************************************************************/
+/* generates test events */
+
+/* context switch */
+#define TRACE_SWITCH(pid) DEV_WRITE_WORD(TRACE_DEV_REG_SWITCH, (pid))
+/* fork */
+#define TRACE_FORK(pid) DEV_WRITE_WORD(TRACE_DEV_REG_FORK, (pid))
+/* clone */
+#define TRACE_CLONE(pid) DEV_WRITE_WORD(TRACE_DEV_REG_CLONE, (pid))
+/* dump name and path of threads executed before trace device created */
+#define TRACE_INIT_NAME(pid,path)\
+do {\
+ DEV_WRITE_WORD(TRACE_DEV_REG_INIT_PID, (pid));\
+ DEV_WRITE_WORD(TRACE_DEV_REG_INIT_NAME, (unsigned long)(path));\
+}while(0)
+/* dump exec mapping of threads executed before trace device created */
+#define TRACE_INIT_EXEC(vstart,vend,eoff,path)\
+do {\
+ DEV_WRITE_WORD(TRACE_DEV_REG_EXECVE_VMSTART, (vstart));\
+ DEV_WRITE_WORD(TRACE_DEV_REG_EXECVE_VMEND, (vend));\
+ DEV_WRITE_WORD(TRACE_DEV_REG_EXECVE_OFFSET, (eoff));\
+ DEV_WRITE_WORD(TRACE_DEV_REG_EXECVE_EXEPATH, (unsigned long)(path));\
+}while(0)
+/* mmap */
+#define TRACE_MMAP(vstart,vend,eoff,path)\
+do {\
+ DEV_WRITE_WORD(TRACE_DEV_REG_EXECVE_VMSTART, (vstart));\
+ DEV_WRITE_WORD(TRACE_DEV_REG_EXECVE_VMEND, (vend));\
+ DEV_WRITE_WORD(TRACE_DEV_REG_EXECVE_OFFSET, (eoff));\
+ DEV_WRITE_WORD(TRACE_DEV_REG_MMAP_EXEPATH, (unsigned long)(path));\
+}while(0)
+/* execve */
+#define TRACE_EXECVE(cmdlen,cmd)\
+do {\
+ DEV_WRITE_WORD(TRACE_DEV_REG_CMDLINE_LEN, (cmdlen));\
+ DEV_WRITE_WORD(TRACE_DEV_REG_CMDLINE, (unsigned long)(cmd));\
+}while(0)
+/* exit */
+#define TRACE_EXIT(retv) DEV_WRITE_WORD(TRACE_DEV_REG_EXIT, (retv))
+
+/* other commands */
+
+/* stop emulation */
+#define TRACE_STOP_EMU() DEV_WRITE_WORD(TRACE_DEV_REG_STOP_EMU, 1)
+/* enable/disable tracing */
+#define TRACE_ENABLE_TRACING() DEV_WRITE_WORD(TRACE_DEV_REG_ENABLE, 1)
+#define TRACE_DISABLE_TRACING() DEV_WRITE_WORD(TRACE_DEV_REG_DISABLE, 1)
+/* dynamic symbols */
+#define TRACE_DYN_SYM(addr,sym)\
+do {\
+ DEV_WRITE_WORD(TRACE_DEV_REG_DYN_SYM_ADDR, (addr));\
+ DEV_WRITE_WORD(TRACE_DEV_REG_DYN_SYM, (unsigned long)(sym));\
+}while(0)
+/* prints */
+#define PRINT_STR(str) DEV_WRITE_WORD(TRACE_DEV_REG_PRINT_STR, (unsigned long)(str))
+#define PRINT_NUM_DEC(num) DEV_WRITE_WORD(TRACE_DEV_REG_PRINT_NUM_DEC, (num))
+#define PRINT_NUM_HEX(num) DEV_WRITE_WORD(TRACE_DEV_REG_PRINT_NUM_HEX, (num))
+
+#endif
diff --git a/emulator/qtools/tests/tests.ld b/emulator/qtools/tests/tests.ld
new file mode 100644
index 0000000..05fe41b
--- /dev/null
+++ b/emulator/qtools/tests/tests.ld
@@ -0,0 +1,10 @@
+SECTIONS {
+ TEXT_START = 0x00010000 ;
+ DATA_START = 0x00200000 ;
+ handlers 0x0 : { *(handlers) }
+ .text TEXT_START : { *(.text) }
+ .data DATA_START : { *(.data) }
+ .bss : { *(.bss) *(COMMON) }
+ p00300000 0x00300000 : { *(p00300000) }
+ p00400000 0x00400000 : { *(p00400000) }
+}
diff --git a/emulator/qtools/thumbdis.cpp b/emulator/qtools/thumbdis.cpp
new file mode 100644
index 0000000..f4294dd
--- /dev/null
+++ b/emulator/qtools/thumbdis.cpp
@@ -0,0 +1,503 @@
+/* Instruction printing code for the ARM
+ Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002
+ Free Software Foundation, Inc.
+ Contributed by Richard Earnshaw (rwe@pegasus.esprit.ec.org)
+ Modification by James G. Smith (jsmith@cygnus.co.uk)
+
+This file is part of libopcodes.
+
+This program is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2 of the License, or (at your option)
+any later version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+/* Modified to fit into the qtools framework. The main differences are:
+ *
+ * - The disassembly function returns a string instead of writing it to a
+ * file stream.
+ *
+ * - All the references to the struct "disassemble_info" have been removed.
+ *
+ * - A set of enums for the thumb opcodes have been defined, along with a
+ * "decode()" function that maps a thumb instruction to an opcode enum.
+ *
+ * - Eliminated uses of the special characters ', `, and ? from the
+ * thumb_opcodes[] table so that we can easily specify separate opcodes
+ * for distinct instructions.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <inttypes.h>
+#include "opcode.h"
+
+
+struct thumb_opcode
+{
+ unsigned short value, mask; /* recognise instruction if (op&mask)==value */
+ Opcode opcode;
+ char * assembler; /* how to disassemble this instruction */
+};
+
+/* format of the assembler string :
+
+ %% %
+ %<bitfield>d print the bitfield in decimal
+ %<bitfield>x print the bitfield in hex
+ %<bitfield>X print the bitfield as 1 hex digit without leading "0x"
+ %<bitfield>r print as an ARM register
+ %<bitfield>f print a floating point constant if >7 else a
+ floating point register
+ %<code>y print a single precision VFP reg.
+ Codes: 0=>Sm, 1=>Sd, 2=>Sn, 3=>multi-list, 4=>Sm pair
+ %<code>z print a double precision VFP reg
+ Codes: 0=>Dm, 1=>Dd, 2=>Dn, 3=>multi-list
+ %c print condition code (always bits 28-31)
+ %P print floating point precision in arithmetic insn
+ %Q print floating point precision in ldf/stf insn
+ %R print floating point rounding mode
+ %<bitnum>'c print specified char iff bit is one
+ %<bitnum>`c print specified char iff bit is zero
+ %<bitnum>?ab print a if bit is one else print b
+ %p print 'p' iff bits 12-15 are 15
+ %t print 't' iff bit 21 set and bit 24 clear
+ %o print operand2 (immediate or register + shift)
+ %a print address for ldr/str instruction
+ %s print address for ldr/str halfword/signextend instruction
+ %b print branch destination
+ %B print arm BLX(1) destination
+ %A print address for ldc/stc/ldf/stf instruction
+ %m print register mask for ldm/stm instruction
+ %C print the PSR sub type.
+ %F print the COUNT field of a LFM/SFM instruction.
+Thumb specific format options:
+ %D print Thumb register (bits 0..2 as high number if bit 7 set)
+ %S print Thumb register (bits 3..5 as high number if bit 6 set)
+ %<bitfield>I print bitfield as a signed decimal
+ (top bit of range being the sign bit)
+ %M print Thumb register mask
+ %N print Thumb register mask (with LR)
+ %O print Thumb register mask (with PC)
+ %T print Thumb condition code (always bits 8-11)
+ %I print cirrus signed shift immediate: bits 0..3|4..6
+ %<bitfield>B print Thumb branch destination (signed displacement)
+ %<bitfield>W print (bitfield * 4) as a decimal
+ %<bitfield>H print (bitfield * 2) as a decimal
+ %<bitfield>a print (bitfield * 4) as a pc-rel offset + decoded symbol
+*/
+
+
+static struct thumb_opcode thumb_opcodes[] =
+{
+ /* Thumb instructions. */
+
+ /* ARM V5 ISA extends Thumb. */
+ {0xbe00, 0xff00, OP_THUMB_BKPT, "bkpt\t%0-7x"},
+ {0x4780, 0xff87, OP_THUMB_BLX, "blx\t%3-6r"}, /* note: 4 bit register number. */
+ /* Format 5 instructions do not update the PSR. */
+ {0x1C00, 0xFFC0, OP_THUMB_MOV, "mov\t%0-2r, %3-5r"},
+ /* Format 4. */
+ {0x4000, 0xFFC0, OP_THUMB_AND, "and\t%0-2r, %3-5r"},
+ {0x4040, 0xFFC0, OP_THUMB_EOR, "eor\t%0-2r, %3-5r"},
+ {0x4080, 0xFFC0, OP_THUMB_LSL, "lsl\t%0-2r, %3-5r"},
+ {0x40C0, 0xFFC0, OP_THUMB_LSR, "lsr\t%0-2r, %3-5r"},
+ {0x4100, 0xFFC0, OP_THUMB_ASR, "asr\t%0-2r, %3-5r"},
+ {0x4140, 0xFFC0, OP_THUMB_ADC, "adc\t%0-2r, %3-5r"},
+ {0x4180, 0xFFC0, OP_THUMB_SBC, "sbc\t%0-2r, %3-5r"},
+ {0x41C0, 0xFFC0, OP_THUMB_ROR, "ror\t%0-2r, %3-5r"},
+ {0x4200, 0xFFC0, OP_THUMB_TST, "tst\t%0-2r, %3-5r"},
+ {0x4240, 0xFFC0, OP_THUMB_NEG, "neg\t%0-2r, %3-5r"},
+ {0x4280, 0xFFC0, OP_THUMB_CMP, "cmp\t%0-2r, %3-5r"},
+ {0x42C0, 0xFFC0, OP_THUMB_CMN, "cmn\t%0-2r, %3-5r"},
+ {0x4300, 0xFFC0, OP_THUMB_ORR, "orr\t%0-2r, %3-5r"},
+ {0x4340, 0xFFC0, OP_THUMB_MUL, "mul\t%0-2r, %3-5r"},
+ {0x4380, 0xFFC0, OP_THUMB_BIC, "bic\t%0-2r, %3-5r"},
+ {0x43C0, 0xFFC0, OP_THUMB_MVN, "mvn\t%0-2r, %3-5r"},
+ /* format 13 */
+ {0xB000, 0xFF80, OP_THUMB_ADD, "add\tsp, #%0-6W"},
+ {0xB080, 0xFF80, OP_THUMB_SUB, "sub\tsp, #%0-6W"},
+ /* format 5 */
+ {0x4700, 0xFF80, OP_THUMB_BX, "bx\t%S"},
+ {0x4400, 0xFF00, OP_THUMB_ADD, "add\t%D, %S"},
+ {0x4500, 0xFF00, OP_THUMB_CMP, "cmp\t%D, %S"},
+ {0x4600, 0xFF00, OP_THUMB_MOV, "mov\t%D, %S"},
+ /* format 14 */
+ {0xB400, 0xFE00, OP_THUMB_PUSH, "push\t%N"},
+ {0xBC00, 0xFE00, OP_THUMB_POP, "pop\t%O"},
+ /* format 2 */
+ {0x1800, 0xFE00, OP_THUMB_ADD, "add\t%0-2r, %3-5r, %6-8r"},
+ {0x1A00, 0xFE00, OP_THUMB_SUB, "sub\t%0-2r, %3-5r, %6-8r"},
+ {0x1C00, 0xFE00, OP_THUMB_ADD, "add\t%0-2r, %3-5r, #%6-8d"},
+ {0x1E00, 0xFE00, OP_THUMB_SUB, "sub\t%0-2r, %3-5r, #%6-8d"},
+ /* format 8 */
+ {0x5200, 0xFE00, OP_THUMB_STRH, "strh\t%0-2r, [%3-5r, %6-8r]"},
+ {0x5A00, 0xFE00, OP_THUMB_LDRH, "ldrh\t%0-2r, [%3-5r, %6-8r]"},
+ {0x5600, 0xFE00, OP_THUMB_LDRSB, "ldrsb\t%0-2r, [%3-5r, %6-8r]"},
+ {0x5E00, 0xFE00, OP_THUMB_LDRSH, "ldrsh\t%0-2r, [%3-5r, %6-8r]"},
+ /* format 7 */
+ {0x5000, 0xFE00, OP_THUMB_STR, "str\t%0-2r, [%3-5r, %6-8r]"},
+ {0x5400, 0xFE00, OP_THUMB_STRB, "strb\t%0-2r, [%3-5r, %6-8r]"},
+ {0x5800, 0xFE00, OP_THUMB_LDR, "ldr\t%0-2r, [%3-5r, %6-8r]"},
+ {0x5C00, 0xFE00, OP_THUMB_LDRB, "ldrb\t%0-2r, [%3-5r, %6-8r]"},
+ /* format 1 */
+ {0x0000, 0xF800, OP_THUMB_LSL, "lsl\t%0-2r, %3-5r, #%6-10d"},
+ {0x0800, 0xF800, OP_THUMB_LSR, "lsr\t%0-2r, %3-5r, #%6-10d"},
+ {0x1000, 0xF800, OP_THUMB_ASR, "asr\t%0-2r, %3-5r, #%6-10d"},
+ /* format 3 */
+ {0x2000, 0xF800, OP_THUMB_MOV, "mov\t%8-10r, #%0-7d"},
+ {0x2800, 0xF800, OP_THUMB_CMP, "cmp\t%8-10r, #%0-7d"},
+ {0x3000, 0xF800, OP_THUMB_ADD, "add\t%8-10r, #%0-7d"},
+ {0x3800, 0xF800, OP_THUMB_SUB, "sub\t%8-10r, #%0-7d"},
+ /* format 6 */
+ /* TODO: Disassemble PC relative "LDR rD,=<symbolic>" */
+ {0x4800, 0xF800, OP_THUMB_LDR, "ldr\t%8-10r, [pc, #%0-7W]\t(%0-7a)"},
+ /* format 9 */
+ {0x6000, 0xF800, OP_THUMB_STR, "str\t%0-2r, [%3-5r, #%6-10W]"},
+ {0x6800, 0xF800, OP_THUMB_LDR, "ldr\t%0-2r, [%3-5r, #%6-10W]"},
+ {0x7000, 0xF800, OP_THUMB_STRB, "strb\t%0-2r, [%3-5r, #%6-10d]"},
+ {0x7800, 0xF800, OP_THUMB_LDRB, "ldrb\t%0-2r, [%3-5r, #%6-10d]"},
+ /* format 10 */
+ {0x8000, 0xF800, OP_THUMB_STRH, "strh\t%0-2r, [%3-5r, #%6-10H]"},
+ {0x8800, 0xF800, OP_THUMB_LDRH, "ldrh\t%0-2r, [%3-5r, #%6-10H]"},
+ /* format 11 */
+ {0x9000, 0xF800, OP_THUMB_STR, "str\t%8-10r, [sp, #%0-7W]"},
+ {0x9800, 0xF800, OP_THUMB_LDR, "ldr\t%8-10r, [sp, #%0-7W]"},
+ /* format 12 */
+ {0xA000, 0xF800, OP_THUMB_ADD, "add\t%8-10r, pc, #%0-7W\t(adr %8-10r,%0-7a)"},
+ {0xA800, 0xF800, OP_THUMB_ADD, "add\t%8-10r, sp, #%0-7W"},
+ /* format 15 */
+ {0xC000, 0xF800, OP_THUMB_STMIA, "stmia\t%8-10r!,%M"},
+ {0xC800, 0xF800, OP_THUMB_LDMIA, "ldmia\t%8-10r!,%M"},
+ /* format 18 */
+ {0xE000, 0xF800, OP_THUMB_B, "b\t%0-10B"},
+ /* format 19 */
+ /* special processing required in disassembler */
+ {0xF000, 0xF800, OP_THUMB_BL, ""},
+ {0xF800, 0xF800, OP_THUMB_BL, "second half of BL instruction %0-15x"},
+ {0xE800, 0xF800, OP_THUMB_BLX, "second half of BLX instruction %0-15x"},
+ /* format 16 */
+ {0xD000, 0xFF00, OP_THUMB_B, "beq\t%0-7B"},
+ {0xD100, 0xFF00, OP_THUMB_B, "bne\t%0-7B"},
+ {0xD200, 0xFF00, OP_THUMB_B, "bcs\t%0-7B"},
+ {0xD300, 0xFF00, OP_THUMB_B, "bcc\t%0-7B"},
+ {0xD400, 0xFF00, OP_THUMB_B, "bmi\t%0-7B"},
+ {0xD500, 0xFF00, OP_THUMB_B, "bpl\t%0-7B"},
+ {0xD600, 0xFF00, OP_THUMB_B, "bvs\t%0-7B"},
+ {0xD700, 0xFF00, OP_THUMB_B, "bvc\t%0-7B"},
+ {0xD800, 0xFF00, OP_THUMB_B, "bhi\t%0-7B"},
+ {0xD900, 0xFF00, OP_THUMB_B, "bls\t%0-7B"},
+ {0xDA00, 0xFF00, OP_THUMB_B, "bge\t%0-7B"},
+ {0xDB00, 0xFF00, OP_THUMB_B, "blt\t%0-7B"},
+ {0xDC00, 0xFF00, OP_THUMB_B, "bgt\t%0-7B"},
+ {0xDD00, 0xFF00, OP_THUMB_B, "ble\t%0-7B"},
+ /* format 17 */
+ {0xDE00, 0xFF00, OP_THUMB_UNDEFINED, "undefined"},
+ {0xDF00, 0xFF00, OP_THUMB_SWI, "swi\t%0-7d"},
+ /* format 9 */
+ {0x6000, 0xF800, OP_THUMB_STR, "str\t%0-2r, [%3-5r, #%6-10W]"},
+ {0x6800, 0xF800, OP_THUMB_LDR, "ldr\t%0-2r, [%3-5r, #%6-10W]"},
+ {0x7000, 0xF800, OP_THUMB_STRB, "strb\t%0-2r, [%3-5r, #%6-10d]"},
+ {0x7800, 0xF800, OP_THUMB_LDRB, "ldrb\t%0-2r, [%3-5r, #%6-10d]"},
+ /* the rest */
+ {0x0000, 0x0000, OP_THUMB_UNDEFINED, "undefined instruction %0-15x"},
+ {0x0000, 0x0000, OP_END, 0}
+};
+
+#define BDISP23(x,y) ((((((x) & 0x07ff) << 11) | ((y) & 0x07ff)) \
+ ^ 0x200000) - 0x200000) /* 23bit */
+
+static char * arm_conditional[] =
+{"eq", "ne", "cs", "cc", "mi", "pl", "vs", "vc",
+ "hi", "ls", "ge", "lt", "gt", "le", "", "nv"};
+
+typedef struct
+{
+ const char * name;
+ const char * description;
+ const char * reg_names[16];
+}
+arm_regname;
+
+static arm_regname regnames[] =
+{
+ { "raw" , "Select raw register names",
+ { "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15"}},
+ { "gcc", "Select register names used by GCC",
+ { "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "sl", "fp", "ip", "sp", "lr", "pc" }},
+ { "std", "Select register names used in ARM's ISA documentation",
+ { "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "sp", "lr", "pc" }},
+ { "apcs", "Select register names used in the APCS",
+ { "a1", "a2", "a3", "a4", "v1", "v2", "v3", "v4", "v5", "v6", "sl", "fp", "ip", "sp", "lr", "pc" }},
+ { "atpcs", "Select register names used in the ATPCS",
+ { "a1", "a2", "a3", "a4", "v1", "v2", "v3", "v4", "v5", "v6", "v7", "v8", "IP", "SP", "LR", "PC" }},
+ { "special-atpcs", "Select special register names used in the ATPCS",
+ { "a1", "a2", "a3", "a4", "v1", "v2", "v3", "WR", "v5", "SB", "SL", "FP", "IP", "SP", "LR", "PC" }}
+};
+
+/* Default to STD register name set. */
+static unsigned int regname_selected = 2;
+
+#define NUM_ARM_REGNAMES NUM_ELEM (regnames)
+#define arm_regnames regnames[regname_selected].reg_names
+
+Opcode decode_insn_thumb(uint32_t given)
+{
+ struct thumb_opcode * insn;
+
+ for (insn = thumb_opcodes; insn->assembler; insn++) {
+ if ((given & insn->mask) == insn->value)
+ return insn->opcode;
+ }
+ return OP_THUMB_UNDEFINED;
+}
+
+// Generates the disassembly string for the thumb instruction "insn1".
+// If "insn1" is a BL or BLX instruction that is the first of two Thumb
+// instructions, then insn2 is the second of two instructions. Otherwise,
+// insn2 is ignored.
+char *disasm_insn_thumb(uint32_t pc, uint32_t insn1, uint32_t insn2, char *result)
+{
+ struct thumb_opcode * insn;
+ static char buf[80];
+ char *ptr;
+ uint32_t addr;
+ int len;
+
+ if (result == NULL)
+ result = buf;
+ ptr = result;
+
+ for (insn = thumb_opcodes; insn->assembler; insn++) {
+ if ((insn1 & insn->mask) != insn->value)
+ continue;
+
+ char * c = insn->assembler;
+
+ /* Special processing for Thumb 2-instruction BL sequence: */
+ if (!*c) { /* Check for empty (not NULL) assembler string. */
+ uint32_t offset;
+
+ offset = BDISP23 (insn1, insn2);
+ offset = offset * 2 + pc + 4;
+
+ if ((insn2 & 0x1000) == 0) {
+ len = sprintf(ptr, "blx\t");
+ offset &= 0xfffffffc;
+ } else {
+ len = sprintf(ptr, "bl\t");
+ }
+ ptr += len;
+
+ sprintf(ptr, "0x%x", offset);
+ return result;
+ }
+
+ insn1 &= 0xffff;
+
+ for (; *c; c++) {
+ if (*c != '%') {
+ len = sprintf(ptr, "%c", *c);
+ ptr += len;
+ continue;
+ }
+
+ int domaskpc = 0;
+ int domasklr = 0;
+
+ switch (*++c) {
+ case '%':
+ len = sprintf(ptr, "%%");
+ ptr += len;
+ break;
+
+ case 'S': {
+ uint32_t reg;
+
+ reg = (insn1 >> 3) & 0x7;
+ if (insn1 & (1 << 6))
+ reg += 8;
+
+ len = sprintf(ptr, "%s", arm_regnames[reg]);
+ ptr += len;
+ break;
+ }
+
+ case 'D': {
+ uint32_t reg;
+
+ reg = insn1 & 0x7;
+ if (insn1 & (1 << 7))
+ reg += 8;
+
+ len = sprintf(ptr, "%s", arm_regnames[reg]);
+ ptr += len;
+ break;
+ }
+
+ case 'T':
+ len = sprintf(ptr, "%s",
+ arm_conditional [(insn1 >> 8) & 0xf]);
+ ptr += len;
+ break;
+
+ case 'N':
+ if (insn1 & (1 << 8))
+ domasklr = 1;
+ /* Fall through. */
+ case 'O':
+ if (*c == 'O' && (insn1 & (1 << 8)))
+ domaskpc = 1;
+ /* Fall through. */
+ case 'M': {
+ int started = 0;
+ int reg;
+
+ len = sprintf(ptr, "{");
+ ptr += len;
+
+ /* It would be nice if we could spot
+ ranges, and generate the rS-rE format: */
+ for (reg = 0; (reg < 8); reg++)
+ if ((insn1 & (1 << reg)) != 0) {
+ if (started) {
+ len = sprintf(ptr, ", ");
+ ptr += len;
+ }
+ started = 1;
+ len = sprintf(ptr, "%s", arm_regnames[reg]);
+ ptr += len;
+ }
+
+ if (domasklr) {
+ if (started) {
+ len = sprintf(ptr, ", ");
+ ptr += len;
+ }
+ started = 1;
+ len = sprintf(ptr, arm_regnames[14] /* "lr" */);
+ ptr += len;
+ }
+
+ if (domaskpc) {
+ if (started) {
+ len = sprintf(ptr, ", ");
+ ptr += len;
+ }
+ len = sprintf(ptr, arm_regnames[15] /* "pc" */);
+ ptr += len;
+ }
+
+ len = sprintf(ptr, "}");
+ ptr += len;
+ break;
+ }
+
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9': {
+ int bitstart = *c++ - '0';
+ int bitend = 0;
+
+ while (*c >= '0' && *c <= '9')
+ bitstart = (bitstart * 10) + *c++ - '0';
+
+ switch (*c) {
+ case '-': {
+ uint32_t reg;
+
+ c++;
+ while (*c >= '0' && *c <= '9')
+ bitend = (bitend * 10) + *c++ - '0';
+ if (!bitend)
+ abort ();
+ reg = insn1 >> bitstart;
+ reg &= (2 << (bitend - bitstart)) - 1;
+ switch (*c) {
+ case 'r':
+ len = sprintf(ptr, "%s", arm_regnames[reg]);
+ break;
+
+ case 'd':
+ len = sprintf(ptr, "%d", reg);
+ break;
+
+ case 'H':
+ len = sprintf(ptr, "%d", reg << 1);
+ break;
+
+ case 'W':
+ len = sprintf(ptr, "%d", reg << 2);
+ break;
+
+ case 'a':
+ /* PC-relative address -- the bottom two
+ bits of the address are dropped
+ before the calculation. */
+ addr = ((pc + 4) & ~3) + (reg << 2);
+ len = sprintf(ptr, "0x%x", addr);
+ break;
+
+ case 'x':
+ len = sprintf(ptr, "0x%04x", reg);
+ break;
+
+ case 'I':
+ reg = ((reg ^ (1 << bitend)) - (1 << bitend));
+ len = sprintf(ptr, "%d", reg);
+ break;
+
+ case 'B':
+ reg = ((reg ^ (1 << bitend)) - (1 << bitend));
+ addr = reg * 2 + pc + 4;
+ len = sprintf(ptr, "0x%x", addr);
+ break;
+
+ default:
+ abort ();
+ }
+ ptr += len;
+ break;
+ }
+
+ case '\'':
+ c++;
+ if ((insn1 & (1 << bitstart)) != 0) {
+ len = sprintf(ptr, "%c", *c);
+ ptr += len;
+ }
+ break;
+
+ case '?':
+ ++c;
+ if ((insn1 & (1 << bitstart)) != 0)
+ len = sprintf(ptr, "%c", *c++);
+ else
+ len = sprintf(ptr, "%c", *++c);
+ ptr += len;
+ break;
+
+ default:
+ abort ();
+ }
+ break;
+ }
+
+ default:
+ abort ();
+ }
+ }
+ return result;
+ }
+
+ /* No match. */
+ abort ();
+}
diff --git a/emulator/qtools/trace_reader.cpp b/emulator/qtools/trace_reader.cpp
new file mode 100644
index 0000000..b38c0b4
--- /dev/null
+++ b/emulator/qtools/trace_reader.cpp
@@ -0,0 +1,1201 @@
+// Copyright 2006 The Android Open Source Project
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <inttypes.h>
+#include <assert.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <elf.h>
+#include "trace_reader.h"
+#include "decoder.h"
+
+// A struct for creating temporary linked-lists of DexSym structs
+struct DexSymList {
+ DexSymList *next;
+ DexSym sym;
+};
+
+// Declare static functions used in this file
+static char *ExtractDexPathFromMmap(const char *mmap_path);
+static void CopyDexSymbolsToArray(DexFileList *dexfile,
+ DexSymList *head, int num_symbols);
+
+// This function creates the pathname to the a specific trace file. The
+// string space is allocated in this routine and must be freed by the
+// caller.
+static char *CreateTracePath(const char *filename, const char *ext)
+{
+ char *fname;
+ const char *base_start, *base_end;
+ int ii, len, base_len, dir_len, path_len, qtrace_len;
+
+ // Handle error cases
+ if (filename == NULL || *filename == 0 || strcmp(filename, "/") == 0)
+ return NULL;
+
+ // Ignore a trailing slash, if any
+ len = strlen(filename);
+ if (filename[len - 1] == '/')
+ len -= 1;
+
+ // Find the basename. We don't use basename(3) because there are
+ // different behaviors for GNU and Posix in the case where the
+ // last character is a slash.
+ base_start = base_end = &filename[len];
+ for (ii = 0; ii < len; ++ii) {
+ base_start -= 1;
+ if (*base_start == '/') {
+ base_start += 1;
+ break;
+ }
+ }
+ base_len = base_end - base_start;
+ dir_len = len - base_len;
+ qtrace_len = strlen("/qtrace");
+
+ // Create space for the pathname: "/dir/basename/qtrace.ext"
+ // The "ext" string already contains the dot, so just add a byte
+ // for the terminating zero.
+ path_len = dir_len + base_len + qtrace_len + strlen(ext) + 1;
+ fname = new char[path_len];
+ if (dir_len > 0)
+ strncpy(fname, filename, dir_len);
+ fname[dir_len] = 0;
+ strncat(fname, base_start, base_len);
+ strcat(fname, "/qtrace");
+ strcat(fname, ext);
+ return fname;
+}
+
+inline BBReader::Future *BBReader::AllocFuture()
+{
+ Future *future = free_;
+ free_ = free_->next;
+ return future;
+}
+
+inline void BBReader::FreeFuture(Future *future)
+{
+ future->next = free_;
+ free_ = future;
+}
+
+inline void BBReader::InsertFuture(Future *future)
+{
+ uint64_t future_time = future->bb.next_time;
+ Future *prev = NULL;
+ Future *ptr;
+ for (ptr = head_; ptr; prev = ptr, ptr = ptr->next) {
+ if (future_time <= ptr->bb.next_time)
+ break;
+ }
+ if (prev == NULL) {
+ // link it at the front
+ future->next = head_;
+ head_ = future;
+ } else {
+ // link it after "prev"
+ future->next = prev->next;
+ prev->next = future;
+ }
+}
+
+// Decodes the next basic block record from the file. Returns 1
+// at end-of-file, otherwise returns 0.
+inline int BBReader::DecodeNextRec()
+{
+ int64_t bb_diff = decoder_->Decode(true);
+ uint64_t time_diff = decoder_->Decode(false);
+ nextrec_.bb_rec.repeat = decoder_->Decode(false);
+ if (time_diff == 0)
+ return 1;
+ if (nextrec_.bb_rec.repeat)
+ nextrec_.bb_rec.time_diff = decoder_->Decode(false);
+ nextrec_.bb_rec.bb_num += bb_diff;
+ nextrec_.bb_rec.start_time += time_diff;
+ return 0;
+}
+
+BBReader::BBReader(TraceReaderBase *trace)
+{
+ trace_ = trace;
+ decoder_ = new Decoder;
+}
+
+BBReader::~BBReader()
+{
+ delete decoder_;
+}
+
+void BBReader::Open(char *filename)
+{
+ // Initialize the class variables
+ memset(&nextrec_, 0, sizeof(TimeRec));
+ memset(futures_, 0, sizeof(Future) * kMaxNumBasicBlocks);
+ head_ = NULL;
+
+ // Link all of the futures_[] array elements on the free list.
+ for (int ii = 0; ii < kMaxNumBasicBlocks - 1; ++ii) {
+ futures_[ii].next = &futures_[ii + 1];
+ }
+ futures_[kMaxNumBasicBlocks - 1].next = 0;
+ free_ = &futures_[0];
+
+ // Open the trace.bb file
+ char *fname = CreateTracePath(filename, ".bb");
+ decoder_->Open(fname);
+ is_eof_ = DecodeNextRec();
+ delete[] fname;
+}
+
+void BBReader::Close()
+{
+ decoder_->Close();
+}
+
+// Returns true at end of file.
+bool BBReader::ReadBB(BBEvent *event)
+{
+ if (is_eof_ && head_ == NULL) {
+ return true;
+ }
+
+#if 0
+ if (nextrec_) {
+ printf("nextrec: buffer[%d], bb_num: %lld start: %d diff %d repeat %d next %u\n",
+ nextrec_ - &buffer_[0],
+ nextrec_->bb_rec.bb_num, nextrec_->bb_rec.start_time,
+ nextrec_->bb_rec.time_diff, nextrec_->bb_rec.repeat,
+ nextrec_->next_time);
+ }
+ if (head_) {
+ printf("head: 0x%x, bb_num: %lld start: %d diff %d repeat %d next %u\n",
+ head_,
+ head_->bb->bb_rec.bb_num, head_->bb->bb_rec.start_time,
+ head_->bb->bb_rec.time_diff, head_->bb->bb_rec.repeat,
+ head_->bb->next_time);
+ }
+#endif
+ if (!is_eof_) {
+ if (head_) {
+ TimeRec *bb = &head_->bb;
+ if (bb->next_time < nextrec_.bb_rec.start_time) {
+ // The head is earlier.
+ event->time = bb->next_time;
+ event->bb_num = bb->bb_rec.bb_num;
+ event->bb_addr = trace_->GetBBAddr(event->bb_num);
+ event->insns = trace_->GetInsns(event->bb_num);
+ event->num_insns = trace_->FindNumInsns(event->bb_num, event->time);
+ event->pid = trace_->FindCurrentPid(event->time);
+ event->is_thumb = trace_->GetIsThumb(event->bb_num);
+
+ // Remove the head element from the list
+ Future *future = head_;
+ head_ = head_->next;
+ if (bb->bb_rec.repeat > 0) {
+ // there are more repetitions of this bb
+ bb->bb_rec.repeat -= 1;
+ bb->next_time += bb->bb_rec.time_diff;
+
+ // Insert this future into the sorted list
+ InsertFuture(future);
+ } else {
+ // Add this future to the free list
+ FreeFuture(future);
+ }
+ return false;
+ }
+ }
+ // The nextrec is earlier (or there was no head)
+ event->time = nextrec_.bb_rec.start_time;
+ event->bb_num = nextrec_.bb_rec.bb_num;
+ event->bb_addr = trace_->GetBBAddr(event->bb_num);
+ event->insns = trace_->GetInsns(event->bb_num);
+ event->num_insns = trace_->FindNumInsns(event->bb_num, event->time);
+ event->pid = trace_->FindCurrentPid(event->time);
+ event->is_thumb = trace_->GetIsThumb(event->bb_num);
+ if (nextrec_.bb_rec.repeat > 0) {
+ Future *future = AllocFuture();
+ future->bb.bb_rec = nextrec_.bb_rec;
+ future->bb.bb_rec.repeat -= 1;
+ future->bb.next_time = nextrec_.bb_rec.start_time + nextrec_.bb_rec.time_diff;
+ InsertFuture(future);
+ }
+
+ is_eof_ = DecodeNextRec();
+ return false;
+ }
+
+ //printf("using head_ 0x%x\n", head_);
+ assert(head_);
+ TimeRec *bb = &head_->bb;
+ event->time = bb->next_time;
+ event->bb_num = bb->bb_rec.bb_num;
+ event->bb_addr = trace_->GetBBAddr(event->bb_num);
+ event->insns = trace_->GetInsns(event->bb_num);
+ event->num_insns = trace_->FindNumInsns(event->bb_num, event->time);
+ event->pid = trace_->FindCurrentPid(event->time);
+ event->is_thumb = trace_->GetIsThumb(event->bb_num);
+
+ // Remove the head element from the list
+ Future *future = head_;
+ head_ = head_->next;
+ if (bb->bb_rec.repeat > 0) {
+ // there are more repetitions of this bb
+ bb->bb_rec.repeat -= 1;
+ bb->next_time += bb->bb_rec.time_diff;
+
+ // Insert this future into the sorted list
+ InsertFuture(future);
+ } else {
+ // Add this future to the free list
+ FreeFuture(future);
+ }
+ return false;
+}
+
+InsnReader::InsnReader()
+{
+ decoder_ = new Decoder;
+}
+
+InsnReader::~InsnReader()
+{
+ delete decoder_;
+}
+
+void InsnReader::Open(char *filename)
+{
+ prev_time_ = 0;
+ time_diff_ = 0;
+ repeat_ = -1;
+
+ // Open the trace.insn file
+ char *fname = CreateTracePath(filename, ".insn");
+ decoder_->Open(fname);
+ delete[] fname;
+}
+
+void InsnReader::Close()
+{
+ decoder_->Close();
+}
+
+uint64_t InsnReader::ReadInsnTime(uint64_t min_time)
+{
+ do {
+ if (repeat_ == -1) {
+ time_diff_ = decoder_->Decode(false);
+ repeat_ = decoder_->Decode(false);
+ }
+ prev_time_ += time_diff_;
+ repeat_ -= 1;
+ } while (prev_time_ < min_time);
+ return prev_time_;
+}
+
+AddrReader::AddrReader()
+{
+ decoder_ = new Decoder;
+ opened_ = false;
+}
+
+AddrReader::~AddrReader()
+{
+ delete decoder_;
+}
+
+// Returns true if there is an error opening the file
+bool AddrReader::Open(char *filename, char *suffix)
+{
+ struct stat stat_buf;
+
+ prev_addr_ = 0;
+ prev_time_ = 0;
+
+ // Open the trace.addr file
+ char *fname = CreateTracePath(filename, suffix);
+ int rval = stat(fname, &stat_buf);
+ if (rval == -1) {
+ // The file does not exist
+ delete[] fname;
+ return true;
+ }
+ decoder_->Open(fname);
+ opened_ = true;
+ delete[] fname;
+ return false;
+}
+
+void AddrReader::Close()
+{
+ decoder_->Close();
+}
+
+// Returns true at end of file.
+bool AddrReader::ReadAddr(uint64_t *time, uint32_t *addr)
+{
+ if (!opened_) {
+ fprintf(stderr, "Cannot read address trace\n");
+ exit(1);
+ }
+ uint32_t addr_diff = decoder_->Decode(true);
+ uint64_t time_diff = decoder_->Decode(false);
+ if (time_diff == 0 && addr_diff == 0) {
+ *addr = 0;
+ *time = 0;
+ return true;
+ }
+ prev_addr_ += addr_diff;
+ prev_time_ += time_diff;
+ *addr = prev_addr_;
+ *time = prev_time_;
+ return false;
+}
+
+ExcReader::ExcReader()
+{
+ decoder_ = new Decoder;
+}
+
+ExcReader::~ExcReader()
+{
+ delete decoder_;
+}
+
+void ExcReader::Open(char *filename)
+{
+ prev_time_ = 0;
+ prev_recnum_ = 0;
+
+ // Open the trace.exc file
+ char *fname = CreateTracePath(filename, ".exc");
+ decoder_->Open(fname);
+ delete[] fname;
+}
+
+void ExcReader::Close()
+{
+ decoder_->Close();
+}
+
+// Returns true at end of file.
+bool ExcReader::ReadExc(uint64_t *time, uint32_t *current_pc, uint64_t *recnum,
+ uint32_t *target_pc, uint64_t *bb_num,
+ uint64_t *bb_start_time, int *num_insns)
+{
+ uint64_t time_diff = decoder_->Decode(false);
+ uint32_t pc = decoder_->Decode(false);
+ if ((time_diff | pc) == 0) {
+ decoder_->Decode(false);
+ decoder_->Decode(false);
+ decoder_->Decode(false);
+ decoder_->Decode(false);
+ decoder_->Decode(false);
+ return true;
+ }
+ uint64_t recnum_diff = decoder_->Decode(false);
+ prev_time_ += time_diff;
+ prev_recnum_ += recnum_diff;
+ *time = prev_time_;
+ *current_pc = pc;
+ *recnum = prev_recnum_;
+ *target_pc = decoder_->Decode(false);
+ *bb_num = decoder_->Decode(false);
+ *bb_start_time = decoder_->Decode(false);
+ *num_insns = decoder_->Decode(false);
+ return false;
+}
+
+PidReader::PidReader()
+{
+ decoder_ = new Decoder;
+}
+
+PidReader::~PidReader()
+{
+ delete decoder_;
+}
+
+void PidReader::Open(char *filename)
+{
+ prev_time_ = 0;
+
+ // Open the trace.pid file
+ char *fname = CreateTracePath(filename, ".pid");
+ decoder_->Open(fname);
+ delete[] fname;
+}
+
+void PidReader::Close()
+{
+ decoder_->Close();
+}
+
+// Returns true at end of file.
+bool PidReader::ReadPidEvent(PidEvent *event)
+{
+ uint64_t time_diff = decoder_->Decode(false);
+ int rec_type = decoder_->Decode(false);
+ prev_time_ += time_diff;
+ event->time = prev_time_;
+ event->rec_type = rec_type;
+ switch(rec_type) {
+ case kPidEndOfFile:
+ return true;
+ case kPidSwitch:
+ case kPidExit:
+ event->pid = decoder_->Decode(false);
+ break;
+ case kPidFork:
+ case kPidClone:
+ event->tgid = decoder_->Decode(false);
+ event->pid = decoder_->Decode(false);
+ break;
+ case kPidMmap:
+ {
+ event->vstart = decoder_->Decode(false);
+ event->vend = decoder_->Decode(false);
+ event->offset = decoder_->Decode(false);
+ int len = decoder_->Decode(false);
+ char *path = new char[len + 1];
+ decoder_->Read(path, len);
+ path[len] = 0;
+ event->path = path;
+ event->mmap_path = path;
+ char *dexfile = ExtractDexPathFromMmap(path);
+ if (dexfile != NULL) {
+ delete[] event->path;
+ event->path = dexfile;
+ }
+ }
+ break;
+ case kPidMunmap:
+ {
+ event->vstart = decoder_->Decode(false);
+ event->vend = decoder_->Decode(false);
+ }
+ break;
+ case kPidSymbolAdd:
+ {
+ event->vstart = decoder_->Decode(false);
+ int len = decoder_->Decode(false);
+ char *path = new char[len + 1];
+ decoder_->Read(path, len);
+ path[len] = 0;
+ event->path = path;
+ }
+ break;
+ case kPidSymbolRemove:
+ event->vstart = decoder_->Decode(false);
+ break;
+ case kPidExec:
+ {
+ int argc = decoder_->Decode(false);
+ event->argc = argc;
+ char **argv = new char*[argc];
+ event->argv = argv;
+ for (int ii = 0; ii < argc; ++ii) {
+ int alen = decoder_->Decode(false);
+ argv[ii] = new char[alen + 1];
+ decoder_->Read(argv[ii], alen);
+ argv[ii][alen] = 0;
+ }
+ }
+ break;
+ case kPidName:
+ case kPidKthreadName:
+ {
+ if (rec_type == kPidKthreadName) {
+ event->tgid = decoder_->Decode(false);
+ }
+ event->pid = decoder_->Decode(false);
+ int len = decoder_->Decode(false);
+ char *path = new char[len + 1];
+ decoder_->Read(path, len);
+ path[len] = 0;
+ event->path = path;
+ }
+ break;
+ }
+ return false;
+}
+
+// Frees the memory that might have been allocated for the given event.
+void PidReader::Dispose(PidEvent *event)
+{
+ switch(event->rec_type) {
+ case kPidMmap:
+ case kPidSymbolAdd:
+ case kPidName:
+ case kPidKthreadName:
+ delete[] event->path;
+ event->path = NULL;
+ event->mmap_path = NULL;
+ break;
+
+ case kPidExec:
+ for (int ii = 0; ii < event->argc; ++ii) {
+ delete[] event->argv[ii];
+ }
+ delete[] event->argv;
+ event->argv = NULL;
+ event->argc = 0;
+ break;
+ }
+}
+
+
+MethodReader::MethodReader()
+{
+ decoder_ = new Decoder;
+ opened_ = false;
+}
+
+MethodReader::~MethodReader()
+{
+ delete decoder_;
+}
+
+bool MethodReader::Open(char *filename)
+{
+ struct stat stat_buf;
+
+ prev_time_ = 0;
+ prev_addr_ = 0;
+ prev_pid_ = 0;
+
+ // Open the trace.method file
+ char *fname = CreateTracePath(filename, ".method");
+ int rval = stat(fname, &stat_buf);
+ if (rval == -1) {
+ // The file does not exist
+ delete[] fname;
+ return true;
+ }
+ decoder_->Open(fname);
+ delete[] fname;
+ opened_ = true;
+ return false;
+}
+
+void MethodReader::Close()
+{
+ decoder_->Close();
+}
+
+// Returns true at end of file.
+bool MethodReader::ReadMethod(MethodRec *method_record)
+{
+ if (!opened_)
+ return true;
+ uint64_t time_diff = decoder_->Decode(false);
+ int32_t addr_diff = decoder_->Decode(true);
+ if (time_diff == 0) {
+ method_record->time = 0;
+ method_record->addr = 0;
+ method_record->flags = 0;
+ return true;
+ }
+ int32_t pid_diff = decoder_->Decode(true);
+ prev_time_ += time_diff;
+ prev_addr_ += addr_diff;
+ prev_pid_ += pid_diff;
+ method_record->time = prev_time_;
+ method_record->addr = prev_addr_;
+ method_record->pid = prev_pid_;
+ method_record->flags = decoder_->Decode(false);
+ return false;
+}
+
+TraceReaderBase::TraceReaderBase()
+{
+ static_filename_ = NULL;
+ static_fstream_ = NULL;
+ header_ = new TraceHeader;
+ bb_reader_ = new BBReader(this);
+ insn_reader_ = new InsnReader;
+ load_addr_reader_ = new AddrReader;
+ store_addr_reader_ = new AddrReader;
+ exc_reader_ = new ExcReader;
+ pid_reader_ = new PidReader;
+ method_reader_ = new MethodReader;
+ internal_exc_reader_ = new ExcReader;
+ internal_pid_reader_ = new PidReader;
+ internal_method_reader_ = new MethodReader;
+ blocks_ = NULL;
+ bb_recnum_ = 0;
+ exc_recnum_ = 0;
+ exc_end_ = false;
+ exc_bb_num_ = 0;
+ exc_time_ = 0;
+ exc_num_insns_ = 0;
+ current_pid_ = 0;
+ next_pid_ = 0;
+ next_pid_switch_time_ = 0;
+ post_processing_ = false;
+ dex_hash_ = NULL;
+ load_eof_ = false;
+ load_time_ = 0;
+ load_addr_ = 0;
+ store_eof_ = false;
+ store_time_ = 0;
+ store_addr_ = 0;
+}
+
+TraceReaderBase::~TraceReaderBase()
+{
+ Close();
+ delete bb_reader_;
+ delete insn_reader_;
+ delete load_addr_reader_;
+ delete store_addr_reader_;
+ delete exc_reader_;
+ delete pid_reader_;
+ delete method_reader_;
+ delete internal_exc_reader_;
+ delete internal_pid_reader_;
+ delete internal_method_reader_;
+ if (blocks_) {
+ int num_static_bb = header_->num_static_bb;
+ for (int ii = 0; ii < num_static_bb; ++ii) {
+ delete[] blocks_[ii].insns;
+ }
+ delete[] blocks_;
+ }
+ delete header_;
+ if (dex_hash_ != NULL) {
+ HashTable<DexFileList*>::entry_type *ptr;
+ for (ptr = dex_hash_->GetFirst(); ptr; ptr = dex_hash_->GetNext()) {
+ DexFileList *dexfile = ptr->value;
+ delete[] dexfile->path;
+ int nsymbols = dexfile->nsymbols;
+ DexSym *symbols = dexfile->symbols;
+ for (int ii = 0; ii < nsymbols; ii++) {
+ delete[] symbols[ii].name;
+ }
+ delete[] dexfile->symbols;
+ delete dexfile;
+ }
+ }
+ delete dex_hash_;
+ delete[] static_filename_;
+}
+
+void TraceReaderBase::ReadTraceHeader(FILE *fstream, char *filename,
+ char *tracename, TraceHeader *header)
+{
+ int rval = fread(header, sizeof(TraceHeader), 1, fstream);
+ if (rval != 1) {
+ perror(filename);
+ exit(1);
+ }
+
+ if (!post_processing_ && strcmp(header->ident, TRACE_IDENT) != 0) {
+ fprintf(stderr, "%s: missing trace header; run 'post_trace %s' first\n",
+ filename, tracename);
+ exit(1);
+ }
+
+ if (header->version != TRACE_VERSION) {
+ fprintf(stderr,
+ "%s: trace header version (%d) does not match compiled tools version (%d)\n",
+ tracename, header->version, TRACE_VERSION);
+ exit(1);
+ }
+
+ convert32(header->version);
+ convert32(header->start_sec);
+ convert32(header->start_usec);
+ convert32(header->pdate);
+ convert32(header->ptime);
+ convert64(header->num_static_bb);
+ convert64(header->num_static_insn);
+ convert64(header->num_dynamic_bb);
+ convert64(header->num_dynamic_insn);
+ convert64(header->elapsed_usecs);
+}
+
+
+void TraceReaderBase::Open(char *filename)
+{
+ char *fname;
+ FILE *fstream;
+
+ // Open the qtrace.bb file
+ bb_reader_->Open(filename);
+
+ // Open the qtrace.insn file
+ insn_reader_->Open(filename);
+
+ // Open the qtrace.load file and read the first line
+ load_eof_ = load_addr_reader_->Open(filename, ".load");
+ if (!load_eof_)
+ load_eof_ = load_addr_reader_->ReadAddr(&load_time_, &load_addr_);
+
+ // Open the qtrace.store file and read the first line
+ store_eof_ = store_addr_reader_->Open(filename, ".store");
+ if (!store_eof_)
+ store_eof_ = store_addr_reader_->ReadAddr(&store_time_, &store_addr_);
+
+ // Open the qtrace.exc file
+ exc_reader_->Open(filename);
+
+ // Open another file stream to the qtrace.exc file for internal reads.
+ // This allows the caller to also read from the qtrace.exc file.
+ internal_exc_reader_->Open(filename);
+
+ // Open the qtrace.pid file
+ pid_reader_->Open(filename);
+ internal_pid_reader_->Open(filename);
+
+ // Open the qtrace.method file
+ method_reader_->Open(filename);
+ internal_method_reader_->Open(filename);
+
+ // Open the qtrace.static file
+ fname = CreateTracePath(filename, ".static");
+ static_filename_ = fname;
+
+ fstream = fopen(fname, "r");
+ if (fstream == NULL) {
+ perror(fname);
+ exit(1);
+ }
+ static_fstream_ = fstream;
+
+ // Read the header
+ ReadTraceHeader(fstream, fname, filename, header_);
+
+ // Allocate space for all of the static blocks
+ int num_static_bb = header_->num_static_bb;
+ if (num_static_bb) {
+ blocks_ = new StaticBlock[num_static_bb];
+
+ // Read in all the static blocks
+ for (int ii = 0; ii < num_static_bb; ++ii) {
+ ReadStatic(&blocks_[ii].rec);
+ int num_insns = blocks_[ii].rec.num_insns;
+ if (num_insns > 0) {
+ blocks_[ii].insns = new uint32_t[num_insns];
+ ReadStaticInsns(num_insns, blocks_[ii].insns);
+ } else {
+ blocks_[ii].insns = NULL;
+ }
+ }
+ fseek(static_fstream_, sizeof(TraceHeader), SEEK_SET);
+ }
+
+ ParseDexList(filename);
+
+ // If the dex_hash_ is NULL, then assign it a small hash table
+ // so that we can simply do a Find() operation without having
+ // to check for NULL first.
+ if (dex_hash_ == NULL) {
+ dex_hash_ = new HashTable<DexFileList*>(1, NULL);
+ }
+}
+
+// Reads the list of pid events looking for an mmap of a dex file.
+PidEvent * TraceReaderBase::FindMmapDexFileEvent()
+{
+ static PidEvent event;
+
+ while (!pid_reader_->ReadPidEvent(&event)) {
+ if (event.rec_type == kPidMmap && event.path != event.mmap_path) {
+ return &event;
+ }
+ pid_reader_->Dispose(&event);
+ }
+ return NULL;
+}
+
+static void CopyDexSymbolsToArray(DexFileList *dexfile,
+ DexSymList *head, int num_symbols)
+{
+ if (dexfile == NULL)
+ return;
+
+ DexSym *symbols = NULL;
+ if (num_symbols > 0) {
+ symbols = new DexSym[num_symbols];
+ }
+ dexfile->nsymbols = num_symbols;
+ dexfile->symbols = symbols;
+
+ // Copy the linked-list to the array.
+ DexSymList *next_sym = NULL;
+ int next_index = 0;
+ for (DexSymList *sym = head; sym; sym = next_sym) {
+ next_sym = sym->next;
+ symbols[next_index].addr = sym->sym.addr;
+ symbols[next_index].len = sym->sym.len;
+ symbols[next_index].name = sym->sym.name;
+ next_index += 1;
+ delete sym;
+ }
+}
+
+void TraceReaderBase::ParseDexList(char *filename)
+{
+ struct stat stat_buf;
+ static const int kBufSize = 4096;
+ char buf[kBufSize];
+ char current_file[kBufSize];
+
+ // Find an example dex file in the list of mmaps
+ PidEvent *event = FindMmapDexFileEvent();
+
+ // Reset the pid_reader to the beginning of the file.
+ pid_reader_->Close();
+ pid_reader_->Open(filename);
+
+ // If there were no mmapped dex files, then there is no need to parse
+ // the dexlist.
+ if (event == NULL)
+ return;
+ char *mmap_dexfile = event->path;
+
+ // Check if the dexlist file exists. It should have the name
+ // "qtrace.dexlist"
+ char *fname = CreateTracePath(filename, ".dexlist");
+ int rval = stat(fname, &stat_buf);
+ if (rval == -1) {
+ // The file does not exist
+ delete[] fname;
+ return;
+ }
+
+ // Open the qtrace.dexlist file
+ FILE *fstream = fopen(fname, "r");
+ if (fstream == NULL) {
+ perror(fname);
+ exit(1);
+ }
+
+ // First pass: read all the filenames, looking for a match for the
+ // example mmap dex filename. Also count the files so that we
+ // know how big to make the hash table.
+ char *match = NULL;
+ int num_files = 0;
+ while (fgets(buf, kBufSize, fstream)) {
+ if (buf[0] != '#')
+ continue;
+ num_files += 1;
+ match = strstr(buf + 1, mmap_dexfile);
+
+ // Check that the dexlist file ends with the string mmap_dexfile.
+ // We add one to the length of the mmap_dexfile because buf[]
+ // ends with a newline. The strlen(mmap_dexfile) computation
+ // could be moved above the loop but it should only ever be
+ // executed once.
+ if (match != NULL && strlen(match) == strlen(mmap_dexfile) + 1)
+ break;
+ }
+
+ // Count the rest of the files
+ while (fgets(buf, kBufSize, fstream)) {
+ if (buf[0] == '#')
+ num_files += 1;
+ }
+
+ if (match == NULL) {
+ fprintf(stderr,
+ "Cannot find the mmapped dex file '%s' in the dexlist\n",
+ mmap_dexfile);
+ exit(1);
+ }
+ delete[] mmap_dexfile;
+
+ // The prefix length includes the leading '#'.
+ int prefix_len = match - buf;
+
+ // Allocate a hash table
+ dex_hash_ = new HashTable<DexFileList*>(4 * num_files, NULL);
+
+ // Reset the file stream to the beginning
+ rewind(fstream);
+
+ // Second pass: read the filenames, stripping off the common prefix.
+ // And read all the (address, method) mappings. When we read a new
+ // filename, create a new DexFileList and add it to the hash table.
+ // Add new symbol mappings to a linked list until we have the whole
+ // list and then create an array for them so that we can use binary
+ // search on the address to find the symbol name quickly.
+
+ // Use a linked list for storing the symbols
+ DexSymList *head = NULL;
+ DexSymList *prev = NULL;
+ int num_symbols = 0;
+
+ DexFileList *dexfile = NULL;
+ int linenum = 0;
+ while (fgets(buf, kBufSize, fstream)) {
+ linenum += 1;
+ if (buf[0] == '#') {
+ // Everything after the '#' is a filename.
+ // Ignore the common prefix.
+
+ // First, save all the symbols from the previous file (if any).
+ CopyDexSymbolsToArray(dexfile, head, num_symbols);
+
+ dexfile = new DexFileList;
+ // Subtract one because buf[] contains a trailing newline
+ int pathlen = strlen(buf) - prefix_len - 1;
+ char *path = new char[pathlen + 1];
+ strncpy(path, buf + prefix_len, pathlen);
+ path[pathlen] = 0;
+ dexfile->path = path;
+ dexfile->nsymbols = 0;
+ dexfile->symbols = NULL;
+ dex_hash_->Update(path, dexfile);
+ num_symbols = 0;
+ head = NULL;
+ prev = NULL;
+ continue;
+ }
+
+ uint32_t addr;
+ int len, line;
+ char clazz[kBufSize], method[kBufSize], sig[kBufSize], file[kBufSize];
+ if (sscanf(buf, "0x%x %d %s %s %s %s %d",
+ &addr, &len, clazz, method, sig, file, &line) != 7) {
+ fprintf(stderr, "Cannot parse line %d of file %s:\n%s",
+ linenum, fname, buf);
+ exit(1);
+ }
+
+ // Concatenate the class name, method name, and signature
+ // plus one for the period separating the class and method.
+ int nchars = strlen(clazz) + strlen(method) + strlen(sig) + 1;
+ char *name = new char[nchars + 1];
+ strcpy(name, clazz);
+ strcat(name, ".");
+ strcat(name, method);
+ strcat(name, sig);
+
+ DexSymList *symbol = new DexSymList;
+ symbol->sym.addr = addr;
+ symbol->sym.len = len;
+ symbol->sym.name = name;
+ symbol->next = NULL;
+
+ // Keep the list in the same order as the file
+ if (head == NULL)
+ head = symbol;
+ if (prev != NULL)
+ prev->next = symbol;
+ prev = symbol;
+ num_symbols += 1;
+ }
+ fclose(fstream);
+
+ // Copy the symbols from the last file.
+ CopyDexSymbolsToArray(dexfile, head, num_symbols);
+ delete[] fname;
+}
+
+// Extracts the pathname to a jar file (or .apk file) from the mmap pathname.
+// An example mmap pathname looks something like this:
+// /data/dalvik-cache/system@app@TestHarness.apk@classes.dex
+// We want to convert that to this:
+// /system/app/TestHarness.apk
+// If the pathname is not of the expected form, then NULL is returned.
+// The space for the extracted path is allocated in this routine and should
+// be freed by the caller after it is no longer needed.
+static char *ExtractDexPathFromMmap(const char *mmap_path)
+{
+ char *end = rindex(mmap_path, '@');
+ if (end == NULL)
+ return NULL;
+ char *start = rindex(mmap_path, '/');
+ if (start == NULL)
+ return NULL;
+ int len = end - start;
+ char *path = new char[len + 1];
+ strncpy(path, start, len);
+ path[len] = 0;
+
+ // Replace all the occurrences of '@' with '/'
+ for (int ii = 0; ii < len; ii++) {
+ if (path[ii] == '@')
+ path[ii] = '/';
+ }
+ return path;
+}
+
+void TraceReaderBase::Close()
+{
+ bb_reader_->Close();
+ insn_reader_->Close();
+ load_addr_reader_->Close();
+ store_addr_reader_->Close();
+ exc_reader_->Close();
+ pid_reader_->Close();
+ method_reader_->Close();
+ internal_exc_reader_->Close();
+ internal_pid_reader_->Close();
+ internal_method_reader_->Close();
+ fclose(static_fstream_);
+ static_fstream_ = NULL;
+}
+
+void TraceReaderBase::WriteHeader(TraceHeader *header)
+{
+ TraceHeader swappedHeader;
+
+ freopen(static_filename_, "r+", static_fstream_);
+ fseek(static_fstream_, 0, SEEK_SET);
+
+ memcpy(&swappedHeader, header, sizeof(TraceHeader));
+
+ convert32(swappedHeader.version);
+ convert32(swappedHeader.start_sec);
+ convert32(swappedHeader.start_usec);
+ convert32(swappedHeader.pdate);
+ convert32(swappedHeader.ptime);
+ convert64(swappedHeader.num_static_bb);
+ convert64(swappedHeader.num_static_insn);
+ convert64(swappedHeader.num_dynamic_bb);
+ convert64(swappedHeader.num_dynamic_insn);
+ convert64(swappedHeader.elapsed_usecs);
+
+ fwrite(&swappedHeader, sizeof(TraceHeader), 1, static_fstream_);
+}
+
+// Reads the next StaticRec from the trace file (not including the list
+// of instructions). On end-of-file, this function returns true.
+int TraceReaderBase::ReadStatic(StaticRec *rec)
+{
+ int rval = fread(rec, sizeof(StaticRec), 1, static_fstream_);
+ if (rval != 1) {
+ if (feof(static_fstream_)) {
+ return true;
+ }
+ perror(static_filename_);
+ exit(1);
+ }
+ convert64(rec->bb_num);
+ convert32(rec->bb_addr);
+ convert32(rec->num_insns);
+ return false;
+}
+
+// Reads "num" instructions into the array "insns" which must be large
+// enough to hold the "num" instructions.
+// Returns the actual number of instructions read. This will usually
+// be "num" but may be less if end-of-file occurred.
+int TraceReaderBase::ReadStaticInsns(int num, uint32_t *insns)
+{
+ if (num == 0)
+ return 0;
+ int rval = fread(insns, sizeof(uint32_t), num, static_fstream_);
+
+ // Convert from little-endian, if necessary
+ for (int ii = 0; ii < num; ++ii)
+ convert32(insns[ii]);
+
+ if (rval != num) {
+ if (feof(static_fstream_)) {
+ return rval;
+ }
+ perror(static_filename_);
+ exit(1);
+ }
+ return rval;
+}
+
+void TraceReaderBase::TruncateLastBlock(uint32_t num_insns)
+{
+ uint32_t insns[kMaxInsnPerBB];
+ StaticRec static_rec;
+ long loc = 0, prev_loc = 0;
+
+ freopen(static_filename_, "r+", static_fstream_);
+ fseek(static_fstream_, sizeof(TraceHeader), SEEK_SET);
+
+ // Find the last record
+ while (1) {
+ prev_loc = loc;
+ loc = ftell(static_fstream_);
+
+ // We don't need to byte-swap static_rec here because we are just
+ // reading the records until we get to the last one.
+ int rval = fread(&static_rec, sizeof(StaticRec), 1, static_fstream_);
+ if (rval != 1)
+ break;
+ ReadStaticInsns(static_rec.num_insns, insns);
+ }
+ if (prev_loc != 0) {
+ fseek(static_fstream_, prev_loc, SEEK_SET);
+ static_rec.num_insns = num_insns;
+
+ // Now we need to byte-swap, but just the field that we changed.
+ convert32(static_rec.num_insns);
+ fwrite(&static_rec, sizeof(StaticRec), 1, static_fstream_);
+ int fd = fileno(static_fstream_);
+ long len = ftell(static_fstream_);
+ len += num_insns * sizeof(uint32_t);
+ ftruncate(fd, len);
+ }
+}
+
+int TraceReaderBase::FindNumInsns(uint64_t bb_num, uint64_t bb_start_time)
+{
+ int num_insns;
+
+ // Read the exception trace file. "bb_recnum_" is the number of
+ // basic block records that have been read so far, and "exc_recnum_"
+ // is the record number from the exception trace.
+ while (!exc_end_ && exc_recnum_ < bb_recnum_) {
+ uint32_t current_pc, target_pc;
+ uint64_t time;
+
+ exc_end_ = internal_exc_reader_->ReadExc(&time, &current_pc, &exc_recnum_,
+ &target_pc, &exc_bb_num_,
+ &exc_time_, &exc_num_insns_);
+ }
+
+ // If an exception occurred in this basic block, then use the
+ // number of instructions specified in the exception record.
+ if (!exc_end_ && exc_recnum_ == bb_recnum_) {
+ num_insns = exc_num_insns_;
+ } else {
+ // Otherwise, use the number of instructions specified in the
+ // static basic block.
+ num_insns = blocks_[bb_num].rec.num_insns;
+ }
+ return num_insns;
+}
+
+// Finds the current pid for the given time. This routine reads the pid
+// trace file and assumes that the "time" parameter is monotonically
+// increasing.
+int TraceReaderBase::FindCurrentPid(uint64_t time)
+{
+ PidEvent event;
+
+ if (time < next_pid_switch_time_)
+ return current_pid_;
+
+ current_pid_ = next_pid_;
+ while (1) {
+ if (internal_pid_reader_->ReadPidEvent(&event)) {
+ next_pid_switch_time_ = ~0ull;
+ break;
+ }
+ if (event.rec_type != kPidSwitch)
+ continue;
+ if (event.time > time) {
+ next_pid_ = event.pid;
+ next_pid_switch_time_ = event.time;
+ break;
+ }
+ current_pid_ = event.pid;
+ }
+ return current_pid_;
+}
diff --git a/emulator/qtools/trace_reader.h b/emulator/qtools/trace_reader.h
new file mode 100644
index 0000000..4123014
--- /dev/null
+++ b/emulator/qtools/trace_reader.h
@@ -0,0 +1,1408 @@
+// Copyright 2006 The Android Open Source Project
+
+#ifndef TRACE_READER_H
+#define TRACE_READER_H
+
+#include <string.h>
+#include <inttypes.h>
+#include <elf.h>
+#include <assert.h>
+#include <cxxabi.h>
+#include "read_elf.h"
+#include "trace_reader_base.h"
+#include "hash_table.h"
+
+struct TraceReaderEmptyStruct {
+};
+
+template <class T = TraceReaderEmptyStruct>
+class TraceReader : public TraceReaderBase {
+ public:
+
+ struct region_entry;
+ typedef struct symbol_entry : public T {
+ typedef region_entry region_type;
+
+ // Define flag values
+ static const uint32_t kIsPlt = 0x01;
+ static const uint32_t kIsVectorStart = 0x02;
+ static const uint32_t kIsVectorTable = (kIsPlt | kIsVectorStart);
+ static const uint32_t kIsInterpreter = 0x04;
+ static const uint32_t kIsMethod = 0x08;
+
+ uint32_t addr;
+
+ // This may hold the name of the interpreted method instead of
+ // the name of the native function if the native function is a
+ // virtual machine interpreter.
+ const char *name;
+
+ // The symbol for the virtual machine interpreter, or NULL
+ symbol_entry *vm_sym;
+ region_type *region;
+ uint32_t flags;
+ } symbol_type;
+
+ typedef struct region_entry {
+ // Define flag values
+ static const uint32_t kIsKernelRegion = 0x01;
+ static const uint32_t kSharedSymbols = 0x02;
+ static const uint32_t kIsLibraryRegion = 0x04;
+ static const uint32_t kIsUserMappedRegion = 0x08;
+
+ region_entry() : refs(0), path(NULL), vstart(0), vend(0), base_addr(0),
+ file_offset(0), flags(0), nsymbols(0), symbols(NULL) {}
+
+ symbol_type *LookupFunctionByName(char *name) {
+ // Just do a linear search
+ for (int ii = 0; ii < nsymbols; ++ii) {
+ if (strcmp(symbols[ii].name, name) == 0)
+ return &symbols[ii];
+ }
+ return NULL;
+ }
+
+ int refs; // reference count
+ char *path;
+ uint32_t vstart;
+ uint32_t vend;
+ uint32_t base_addr;
+ uint32_t file_offset;
+ uint32_t flags;
+ int nsymbols;
+ symbol_type *symbols;
+ } region_type;
+
+ typedef typename HashTable<region_type*>::entry_type hash_entry_type;
+
+ class ProcessState {
+ public:
+
+ // The "regions" array below is a pointer to array of pointers to
+ // regions. The size of the pointer array is kInitialNumRegions,
+ // but grows if needed. There is a separate region for each mmap
+ // call which includes shared libraries as well as .dex and .jar
+ // files. In addition, there is a region for the main executable
+ // for this process, as well as a few regions for the kernel.
+ //
+ // If a child process is a clone of a parent process, the
+ // regions array is unused. Instead, the "addr_manager" pointer is
+ // used to find the process that is the address space manager for
+ // both the parent and child processes.
+ static const int kInitialNumRegions = 10;
+
+ static const int kMaxMethodStackSize = 1000;
+
+ // Define values for the ProcessState flag bits
+ static const int kCalledExec = 0x01;
+ static const int kCalledExit = 0x02;
+ static const int kIsClone = 0x04;
+ static const int kHasKernelRegion = 0x08;
+ static const int kHasFirstMmap = 0x10;
+
+ ProcessState() {
+ cpu_time = 0;
+ tgid = 0;
+ pid = 0;
+ parent_pid = 0;
+ exit_val = 0;
+ flags = 0;
+ argc = 0;
+ argv = NULL;
+ name = NULL;
+ nregions = 0;
+ max_regions = 0;
+ // Don't allocate space yet until we know if we are a clone.
+ regions = NULL;
+ parent = NULL;
+ addr_manager = this;
+ next = NULL;
+ current_method_sym = NULL;
+ method_stack_top = 0;
+ }
+
+ ~ProcessState() {
+ delete[] name;
+ if ((flags & kIsClone) != 0) {
+ return;
+ }
+
+ // Free the regions. We must be careful not to free the symbols
+ // within each region because the symbols are sometimes shared
+ // between multiple regions. The TraceReader class has a hash
+ // table containing all the unique regions and it will free the
+ // region symbols in its destructor. We need to free only the
+ // regions and the array of region pointers.
+ //
+ // Each region is also reference-counted. The count is zero
+ // if no other processes are sharing this region.
+ for (int ii = 0; ii < nregions; ii++) {
+ if (regions[ii]->refs > 0) {
+ regions[ii]->refs -= 1;
+ continue;
+ }
+
+ delete regions[ii];
+ }
+
+ delete[] regions;
+
+ for (int ii = 0; ii < argc; ++ii)
+ delete[] argv[ii];
+ delete[] argv;
+ }
+
+ // Dumps the stack contents to standard output. For debugging.
+ void DumpStack();
+
+ uint64_t cpu_time;
+ uint64_t start_time;
+ uint64_t end_time;
+ int tgid;
+ int pid;
+ int parent_pid;
+ int exit_val;
+ uint32_t flags;
+ int argc;
+ char **argv;
+ char *name;
+ int nregions; // num regions in use
+ int max_regions; // max regions allocated
+ region_type **regions;
+ ProcessState *parent;
+ ProcessState *addr_manager; // the address space manager process
+ ProcessState *next;
+ int method_stack_top;
+ uint32_t method_stack[kMaxMethodStackSize];
+ symbol_type *current_method_sym;
+ };
+
+ TraceReader();
+ ~TraceReader();
+
+ void ReadKernelSymbols(const char *kernel_file);
+ void CopyKernelRegion(ProcessState *pstate);
+ void ClearRegions(ProcessState *pstate);
+ void CopyRegions(ProcessState *parent, ProcessState *child);
+ symbol_type *LookupFunction(int pid, uint32_t addr, uint64_t time);
+ symbol_type *GetSymbols(int *num_syms);
+ ProcessState *GetCurrentProcess() { return current_; }
+ ProcessState *GetProcesses(int *num_procs);
+ ProcessState *GetNextProcess();
+ char *GetProcessName(int pid);
+ void SetRoot(const char *root) { root_ = root; }
+ void SetDemangle(bool demangle) { demangle_ = demangle; }
+ bool ReadMethodSymbol(MethodRec *method_record,
+ symbol_type **psym,
+ ProcessState **pproc);
+
+ protected:
+ virtual int FindCurrentPid(uint64_t time);
+
+ private:
+
+ static const int kNumPids = 32768;
+ static const uint32_t kIncludeLocalSymbols = 0x1;
+
+ void AddPredefinedRegion(region_type *region, const char *path,
+ uint32_t vstart, uint32_t vend,
+ uint32_t base);
+ void InitRegionSymbols(region_type *region, int nsymbols);
+ void AddRegionSymbol(region_type *region, int idx,
+ uint32_t addr, const char *name,
+ uint32_t flags);
+ void AddPredefinedRegions(ProcessState *pstate);
+ void demangle_names(int nfuncs, symbol_type *functions);
+ bool ReadElfSymbols(region_type *region, uint32_t flags);
+ void AddRegion(ProcessState *pstate, region_type *region);
+ region_type *FindRegion(uint32_t addr, int nregions,
+ region_type **regions);
+ symbol_type *FindFunction(uint32_t addr, int nsyms,
+ symbol_type *symbols, bool exact_match);
+ symbol_type *FindCurrentMethod(int pid, uint64_t time);
+ void PopulateSymbolsFromDexFile(const DexFileList *dexfile,
+ region_type *region);
+ void HandlePidEvent(PidEvent *event);
+ void HandleMethodRecord(ProcessState *pstate,
+ MethodRec *method_rec);
+
+ int cached_pid_;
+ symbol_type *cached_func_;
+ symbol_type unknown_;
+ int next_pid_;
+
+ PidEvent next_pid_event_;
+ ProcessState *processes_[kNumPids];
+ ProcessState *current_;
+ MethodRec next_method_;
+ uint64_t function_start_time_;
+ const char *root_;
+ HashTable<region_type*> *hash_;
+ bool demangle_;
+};
+
+template<class T>
+TraceReader<T>::TraceReader()
+{
+ static PidEvent event_no_action;
+
+ cached_pid_ = -1;
+ cached_func_ = NULL;
+
+ memset(&unknown_, 0, sizeof(symbol_type));
+ unknown_.name = "(unknown)";
+ next_pid_ = 0;
+
+ memset(&event_no_action, 0, sizeof(PidEvent));
+ event_no_action.rec_type = kPidNoAction;
+ next_pid_event_ = event_no_action;
+ for (int ii = 1; ii < kNumPids; ++ii)
+ processes_[ii] = NULL;
+ current_ = new ProcessState;
+ processes_[0] = current_;
+ next_method_.time = 0;
+ next_method_.addr = 0;
+ next_method_.flags = 0;
+ function_start_time_ = 0;
+ root_ = "";
+ hash_ = new HashTable<region_type*>(512);
+ AddPredefinedRegions(current_);
+ demangle_ = true;
+}
+
+template<class T>
+TraceReader<T>::~TraceReader()
+{
+ hash_entry_type *ptr;
+ for (ptr = hash_->GetFirst(); ptr; ptr = hash_->GetNext()) {
+ region_type *region = ptr->value;
+ int nsymbols = region->nsymbols;
+ for (int ii = 0; ii < nsymbols; ii++) {
+ delete[] region->symbols[ii].name;
+ }
+ delete[] region->symbols;
+ delete[] region->path;
+
+ // Do not delete the region itself here. Each region
+ // is reference-counted and deleted by the ProcessState
+ // object that owns it.
+ }
+ delete hash_;
+
+ // Delete the ProcessState objects after the region symbols in
+ // the hash table above so that we still have valid region pointers
+ // when deleting the region symbols.
+ for (int ii = 0; ii < kNumPids; ++ii) {
+ delete processes_[ii];
+ }
+}
+
+// This function is used by the qsort() routine to sort symbols
+// into increasing address order.
+template<class T>
+int cmp_symbol_addr(const void *a, const void *b) {
+ typedef typename TraceReader<T>::symbol_type stype;
+
+ const stype *syma = static_cast<stype const *>(a);
+ const stype *symb = static_cast<stype const *>(b);
+ uint32_t addr1 = syma->addr;
+ uint32_t addr2 = symb->addr;
+ if (addr1 < addr2)
+ return -1;
+ if (addr1 > addr2)
+ return 1;
+
+ // The addresses are the same, sort the symbols into
+ // increasing alphabetical order. But put symbols that
+ // that start with "_" last.
+ if (syma->name[0] == '_' || symb->name[0] == '_') {
+ // Count the number of leading underscores and sort the
+ // symbol with the most underscores last.
+ int aCount = 0;
+ while (syma->name[aCount] == '_')
+ aCount += 1;
+ int bCount = 0;
+ while (symb->name[bCount] == '_')
+ bCount += 1;
+ if (aCount < bCount) {
+ return -1;
+ }
+ if (aCount > bCount) {
+ return 1;
+ }
+ // If the symbols have the same number of underscores, then
+ // fall through and sort by the whole name.
+ }
+ return strcmp(syma->name, symb->name);
+}
+
+// This function is used by the qsort() routine to sort region entries
+// into increasing address order.
+template<class T>
+int cmp_region_addr(const void *a, const void *b) {
+ typedef typename TraceReader<T>::region_type rtype;
+
+ const rtype *ma = *static_cast<rtype* const *>(a);
+ const rtype *mb = *static_cast<rtype* const *>(b);
+ uint32_t addr1 = ma->vstart;
+ uint32_t addr2 = mb->vstart;
+ if (addr1 < addr2)
+ return -1;
+ if (addr1 == addr2)
+ return 0;
+ return 1;
+}
+
+// This routine returns a new array containing all the symbols.
+template<class T>
+typename TraceReader<T>::symbol_type*
+TraceReader<T>::GetSymbols(int *num_syms)
+{
+ // Count the symbols
+ int nsyms = 0;
+ for (hash_entry_type *ptr = hash_->GetFirst(); ptr; ptr = hash_->GetNext()) {
+ region_type *region = ptr->value;
+ nsyms += region->nsymbols;
+ }
+ *num_syms = nsyms;
+
+ // Allocate space
+ symbol_type *syms = new symbol_type[nsyms];
+ symbol_type *next_sym = syms;
+
+ // Copy the symbols
+ for (hash_entry_type *ptr = hash_->GetFirst(); ptr; ptr = hash_->GetNext()) {
+ region_type *region = ptr->value;
+ memcpy(next_sym, region->symbols, region->nsymbols * sizeof(symbol_type));
+ next_sym += region->nsymbols;
+ }
+
+ return syms;
+}
+
+// This routine returns all the valid processes.
+template<class T>
+typename TraceReader<T>::ProcessState*
+TraceReader<T>::GetProcesses(int *num_procs)
+{
+ // Count the valid processes
+ int nprocs = 0;
+ for (int ii = 0; ii < kNumPids; ++ii) {
+ if (processes_[ii])
+ nprocs += 1;
+ }
+
+ // Allocate a new array to hold the valid processes.
+ ProcessState *procs = new ProcessState[nprocs];
+
+ // Copy the processes to the new array.
+ ProcessState *pstate = procs;
+ for (int ii = 0; ii < kNumPids; ++ii) {
+ if (processes_[ii])
+ memcpy(pstate++, processes_[ii], sizeof(ProcessState));
+ }
+
+ *num_procs = nprocs;
+ return procs;
+}
+
+// This routine returns the next valid process, or NULL if there are no
+// more valid processes.
+template<class T>
+typename TraceReader<T>::ProcessState*
+TraceReader<T>::GetNextProcess()
+{
+ while (next_pid_ < kNumPids) {
+ if (processes_[next_pid_])
+ return processes_[next_pid_++];
+ next_pid_ += 1;
+ }
+ next_pid_ = 0;
+ return NULL;
+}
+
+template<class T>
+char* TraceReader<T>::GetProcessName(int pid)
+{
+ if (pid < 0 || pid >= kNumPids || processes_[pid] == NULL)
+ return "(unknown)";
+ return processes_[pid]->name;
+}
+
+template<class T>
+void TraceReader<T>::AddPredefinedRegion(region_type *region, const char *path,
+ uint32_t vstart, uint32_t vend,
+ uint32_t base)
+{
+ // Copy the path to make it easy to delete later.
+ int len = strlen(path);
+ region->path = new char[len + 1];
+ strcpy(region->path, path);
+ region->vstart = vstart;
+ region->vend = vend;
+ region->base_addr = base;
+ region->flags = region_type::kIsKernelRegion;
+}
+
+template<class T>
+void TraceReader<T>::InitRegionSymbols(region_type *region, int nsymbols)
+{
+ region->nsymbols = nsymbols;
+ region->symbols = new symbol_type[nsymbols];
+ memset(region->symbols, 0, nsymbols * sizeof(symbol_type));
+}
+
+template<class T>
+void TraceReader<T>::AddRegionSymbol(region_type *region, int idx,
+ uint32_t addr, const char *name,
+ uint32_t flags)
+{
+ region->symbols[idx].addr = addr;
+ region->symbols[idx].name = Strdup(name);
+ region->symbols[idx].vm_sym = NULL;
+ region->symbols[idx].region = region;
+ region->symbols[idx].flags = flags;
+}
+
+template<class T>
+void TraceReader<T>::AddPredefinedRegions(ProcessState *pstate)
+{
+ region_type *region = new region_type;
+ AddPredefinedRegion(region, "(bootloader)", 0, 0x14, 0);
+ InitRegionSymbols(region, 2);
+ AddRegionSymbol(region, 0, 0, "(bootloader_start)", 0);
+ AddRegionSymbol(region, 1, 0x14, "(bootloader_end)", 0);
+ AddRegion(pstate, region);
+ hash_->Update(region->path, region);
+
+ region = new region_type;
+ AddPredefinedRegion(region, "(exception vectors)", 0xffff0000, 0xffff0500,
+ 0xffff0000);
+ InitRegionSymbols(region, 2);
+ AddRegionSymbol(region, 0, 0x0, "(vector_start)",
+ symbol_type::kIsVectorStart);
+ AddRegionSymbol(region, 1, 0x500, "(vector_end)", 0);
+ AddRegion(pstate, region);
+ hash_->Update(region->path, region);
+
+ region = new region_type;
+ AddPredefinedRegion(region, "(atomic ops)", 0xffff0f80, 0xffff1000,
+ 0xffff0f80);
+ // Mark this region as also being mapped in user-space.
+ // This isn't used anywhere in this code but client code can test for
+ // this flag and decide whether to treat this as kernel or user code.
+ region->flags |= region_type::kIsUserMappedRegion;
+
+ InitRegionSymbols(region, 4);
+ AddRegionSymbol(region, 0, 0x0, "(kuser_atomic_inc)", 0);
+ AddRegionSymbol(region, 1, 0x20, "(kuser_atomic_dec)", 0);
+ AddRegionSymbol(region, 2, 0x40, "(kuser_cmpxchg)", 0);
+ AddRegionSymbol(region, 3, 0x80, "(kuser_end)", 0);
+ AddRegion(pstate, region);
+ hash_->Update(region->path, region);
+}
+
+template<class T>
+void TraceReader<T>::ReadKernelSymbols(const char *kernel_file)
+{
+ region_type *region = new region_type;
+ // Copy the path to make it easy to delete later.
+ int len = strlen(kernel_file);
+ region->path = new char[len + 1];
+ strcpy(region->path, kernel_file);
+ region->flags = region_type::kIsKernelRegion;
+ ReadElfSymbols(region, kIncludeLocalSymbols);
+ region->vend = 0xffff0000;
+ AddRegion(processes_[0], region);
+ processes_[0]->flags |= ProcessState::kHasKernelRegion;
+ hash_->Update(region->path, region);
+}
+
+template<class T>
+void TraceReader<T>::demangle_names(int nfuncs, symbol_type *functions)
+{
+ char *demangled;
+ int status;
+
+ for (int ii = 0; ii < nfuncs; ++ii) {
+ demangled = NULL;
+ int len = strlen(functions[ii].name);
+
+ // If we don't check for "len > 1" then the demangler will incorrectly
+ // expand 1-letter function names. For example, "b" becomes "bool",
+ // "c" becomes "char" and "d" becomes "double". Also check that the
+ // first character is an underscore. Otherwise, on some strings
+ // the demangler will try to read past the end of the string (because
+ // the string is not really a C++ mangled name) and valgrind will
+ // complain.
+ if (demangle_ && len > 1 && functions[ii].name[0] == '_') {
+ demangled = abi::__cxa_demangle(functions[ii].name, 0, NULL,
+ &status);
+ }
+
+ if (demangled != NULL) {
+ delete[] functions[ii].name;
+ functions[ii].name = Strdup(demangled);
+ free(demangled);
+ }
+ }
+}
+
+// Adds the symbols from the given ELF file to the given process.
+// Returns false if the file was not an ELF file or if there was an
+// error trying to read the sections of the ELF file.
+template<class T>
+bool TraceReader<T>::ReadElfSymbols(region_type *region, uint32_t flags)
+{
+ static char full_path[4096];
+ Elf32_Shdr *symtab, *symstr;
+ Elf32_Ehdr *hdr;
+ Elf32_Shdr *shdr;
+
+ full_path[0] = 0;
+ if (root_ && strcmp(root_, "/")) {
+ strcpy(full_path, root_);
+ }
+ strcat(full_path, region->path);
+ FILE *fobj = fopen(full_path, "r");
+ if(fobj == NULL) {
+ EmptyRegion:
+ // we need to create an (unknown) symbol with address 0, otherwise some
+ // other parts of the trace reader will simply crash when dealing with
+ // an empty region
+ region->vstart = 0;
+ region->nsymbols = 1;
+ region->symbols = new symbol_type[1];
+ memset(region->symbols, 0, sizeof(symbol_type));
+
+ region->symbols[0].addr = 0;
+ region->symbols[0].name = Strdup("(unknown)");
+ region->symbols[0].vm_sym = NULL;
+ region->symbols[0].region = region;
+ region->symbols[0].flags = 0;
+
+ if (fobj != NULL)
+ fclose(fobj);
+ return false;
+ }
+
+ hdr = ReadElfHeader(fobj);
+ if (hdr == NULL) {
+ fprintf(stderr, "Cannot read ELF header from '%s'\n", full_path);
+ goto EmptyRegion;
+ }
+
+ shdr = ReadSectionHeaders(hdr, fobj);
+ if(shdr == NULL) {
+ fprintf(stderr, "Can't read section headers from executable\n");
+ goto EmptyRegion;
+ }
+ char *section_names = ReadStringTable(hdr, shdr, fobj);
+
+ // Get the symbol table section
+ symtab = FindSymbolTableSection(hdr, shdr, section_names);
+ if (symtab == NULL || symtab->sh_size == 0) {
+ fprintf(stderr, "Can't read symbol table from '%s'\n", full_path);
+ goto EmptyRegion;
+ }
+
+ // Get the symbol string table section
+ symstr = FindSymbolStringTableSection(hdr, shdr, section_names);
+ if (symstr == NULL || symstr->sh_size == 0) {
+ fprintf(stderr, "Can't read symbol string table from '%s'\n", full_path);
+ goto EmptyRegion;
+ }
+
+ // Load the symbol string table data
+ char *symbol_names = new char[symstr->sh_size];
+ ReadSection(symstr, symbol_names, fobj);
+
+ int num_entries = symtab->sh_size / symtab->sh_entsize;
+ Elf32_Sym *elf_symbols = new Elf32_Sym[num_entries];
+ ReadSection(symtab, elf_symbols, fobj);
+ AdjustElfSymbols(hdr, elf_symbols, num_entries);
+#if 0
+ printf("size: %d, ent_size: %d, num_entries: %d\n",
+ symtab->sh_size, symtab->sh_entsize, num_entries);
+#endif
+ int nfuncs = 0;
+
+ // Allocate space for all of the symbols for now. We will
+ // reallocate space for just the function symbols after we
+ // know how many there are. Also, make sure there is room
+ // for some extra symbols, including the text section names.
+ int num_alloc = num_entries + hdr->e_shnum + 1;
+ symbol_type *func_symbols = new symbol_type[num_alloc];
+ memset(func_symbols, 0, num_alloc * sizeof(symbol_type));
+
+ // If this is the shared library for a virtual machine, then
+ // set the IsInterpreter flag for all symbols in that shared library.
+ // This will allow us to replace the symbol names with the name of
+ // the currently executing method on the virtual machine.
+ int symbol_flags = 0;
+ char *cp = strrchr(region->path, '/');
+ if (cp != NULL) {
+ // Move past the '/'
+ cp += 1;
+ } else {
+ // There was no '/', so use the whole path
+ cp = region->path;
+ }
+ if (strcmp(cp, "libdvm.so") == 0) {
+ symbol_flags = symbol_type::kIsInterpreter;
+ }
+
+ bool zero_found = false;
+ for (int ii = 1; ii < num_entries; ++ii) {
+ int idx = elf_symbols[ii].st_name;
+
+ // If the symbol does not have a name, or if the name starts with a
+ // dollar sign ($), then skip it.
+ if (idx == 0 || symbol_names[idx] == 0 || symbol_names[idx] == '$')
+ continue;
+
+ // If the section index is not executable, then skip it.
+ uint32_t section = elf_symbols[ii].st_shndx;
+ if (section == 0 || section >= hdr->e_shnum)
+ continue;
+ if ((shdr[section].sh_flags & SHF_EXECINSTR) == 0)
+ continue;
+
+ uint8_t sym_type = ELF32_ST_TYPE(elf_symbols[ii].st_info);
+ uint8_t sym_bind = ELF32_ST_BIND(elf_symbols[ii].st_info);
+
+ // Allow the caller to decide if we want local non-function
+ // symbols to be included. We currently include these symbols
+ // only for the kernel, where it is useful because the kernel
+ // has lots of assembly language labels that have meaningful names.
+ if ((flags & kIncludeLocalSymbols) == 0 && sym_bind == STB_LOCAL
+ && sym_type != STT_FUNC) {
+ continue;
+ }
+#if 0
+ printf("%08x %x %x %s\n",
+ elf_symbols[ii].st_value,
+ sym_bind,
+ sym_type,
+ &symbol_names[idx]);
+#endif
+ if (sym_type != STT_FUNC && sym_type != STT_NOTYPE)
+ continue;
+
+ if (elf_symbols[ii].st_value == 0)
+ zero_found = true;
+
+ // The address of thumb functions seem to have the low bit set,
+ // even though the instructions are really at an even address.
+ uint32_t addr = elf_symbols[ii].st_value & ~0x1;
+ func_symbols[nfuncs].addr = addr;
+ func_symbols[nfuncs].name = Strdup(&symbol_names[idx]);
+ func_symbols[nfuncs].flags = symbol_flags;
+
+ nfuncs += 1;
+ }
+
+ // Add a [0, "(unknown)"] symbol pair if there is not already a
+ // symbol with the address zero. We don't need to reallocate space
+ // because we already have more than we need.
+ if (!zero_found) {
+ func_symbols[nfuncs].addr = 0;
+ func_symbols[nfuncs].name = Strdup("(0 unknown)");
+ nfuncs += 1;
+ }
+
+ // Add another entry at the end
+ func_symbols[nfuncs].addr = 0xffffffff;
+ func_symbols[nfuncs].name = Strdup("(end)");
+ nfuncs += 1;
+
+ // Add in the names of the text sections, but only if there
+ // are no symbols with that address already.
+ for (int section = 0; section < hdr->e_shnum; ++section) {
+ if ((shdr[section].sh_flags & SHF_EXECINSTR) == 0)
+ continue;
+
+ uint32_t addr = shdr[section].sh_addr;
+ // Search for a symbol with a matching address. The symbols aren't
+ // sorted yet so we just search the whole list.
+ int ii;
+ for (ii = 0; ii < nfuncs; ++ii) {
+ if (addr == func_symbols[ii].addr)
+ break;
+ }
+ if (ii == nfuncs) {
+ // Symbol at address "addr" does not exist, so add the text
+ // section name. This will usually add the ".plt" section
+ // (procedure linkage table).
+ int idx = shdr[section].sh_name;
+ func_symbols[nfuncs].addr = addr;
+ func_symbols[nfuncs].name = Strdup(&section_names[idx]);
+ if (strcmp(func_symbols[nfuncs].name, ".plt") == 0) {
+ func_symbols[nfuncs].flags |= symbol_type::kIsPlt;
+ // Change the name of the symbol to include the
+ // name of the library. Otherwise we will have lots
+ // of ".plt" symbols.
+ int len = strlen(region->path);
+ len += strlen(":.plt");
+ char *name = new char[len + 1];
+ strcpy(name, region->path);
+ strcat(name, ":.plt");
+ delete[] func_symbols[nfuncs].name;
+ func_symbols[nfuncs].name = name;
+
+ // Check if this is part of the virtual machine interpreter
+ char *cp = strrchr(region->path, '/');
+ if (cp != NULL) {
+ // Move past the '/'
+ cp += 1;
+ } else {
+ // There was no '/', so use the whole path
+ cp = region->path;
+ }
+ if (strcmp(cp, "libdvm.so") == 0) {
+ func_symbols[nfuncs].flags |= symbol_type::kIsInterpreter;
+ }
+ }
+ nfuncs += 1;
+ }
+ }
+
+ // Allocate just the space we need now that we know exactly
+ // how many symbols we have.
+ symbol_type *functions = new symbol_type[nfuncs];
+
+ // Copy the symbols to the functions array
+ memcpy(functions, func_symbols, nfuncs * sizeof(symbol_type));
+ delete[] func_symbols;
+
+ // Assign the region pointers
+ for (int ii = 0; ii < nfuncs; ++ii) {
+ functions[ii].region = region;
+ }
+
+ // Sort the symbols into increasing address order
+ qsort(functions, nfuncs, sizeof(symbol_type), cmp_symbol_addr<T>);
+
+ // If there are multiple symbols with the same address, then remove
+ // the duplicates. First, count the number of duplicates.
+ uint32_t prev_addr = ~0;
+ int num_duplicates = 0;
+ for (int ii = 0; ii < nfuncs; ++ii) {
+ if (prev_addr == functions[ii].addr)
+ num_duplicates += 1;
+ prev_addr = functions[ii].addr;
+ }
+
+ if (num_duplicates > 0) {
+ int num_uniq = nfuncs - num_duplicates;
+
+ // Allocate space for the unique functions
+ symbol_type *uniq_functions = new symbol_type[num_uniq];
+
+ // Copy the unique functions
+ prev_addr = ~0;
+ int next_uniq = 0;
+ for (int ii = 0; ii < nfuncs; ++ii) {
+ if (prev_addr == functions[ii].addr) {
+ delete[] functions[ii].name;
+ continue;
+ }
+ memcpy(&uniq_functions[next_uniq++], &functions[ii],
+ sizeof(symbol_type));
+ prev_addr = functions[ii].addr;
+ }
+ assert(next_uniq == num_uniq);
+
+ delete[] functions;
+ functions = uniq_functions;
+ nfuncs = num_uniq;
+ }
+
+ // Finally, demangle all of the symbol names
+ demangle_names(nfuncs, functions);
+
+ uint32_t min_addr = 0;
+ if (!zero_found)
+ min_addr = functions[1].addr;
+ if (region->vstart == 0)
+ region->vstart = min_addr;
+ region->nsymbols = nfuncs;
+ region->symbols = functions;
+
+#if 0
+ printf("%s num symbols: %d min_addr: 0x%x\n", region->path, nfuncs, min_addr);
+ for (int ii = 0; ii < nfuncs; ++ii) {
+ printf("0x%08x %s\n", functions[ii].addr, functions[ii].name);
+ }
+#endif
+ delete[] elf_symbols;
+ delete[] symbol_names;
+ delete[] section_names;
+ delete[] shdr;
+ delete hdr;
+ fclose(fobj);
+
+ return true;
+}
+
+template<class T>
+void TraceReader<T>::CopyKernelRegion(ProcessState *pstate)
+{
+ ProcessState *manager = pstate->addr_manager;
+ if (manager->flags & ProcessState::kHasKernelRegion)
+ return;
+
+ int nregions = processes_[0]->nregions;
+ region_type **regions = processes_[0]->regions;
+ for (int ii = 0; ii < nregions; ii++) {
+ if (regions[ii]->flags & region_type::kIsKernelRegion) {
+ AddRegion(manager, regions[ii]);
+ regions[ii]->refs += 1;
+ }
+ }
+ manager->flags |= ProcessState::kHasKernelRegion;
+}
+
+template<class T>
+void TraceReader<T>::ClearRegions(ProcessState *pstate)
+{
+ assert(pstate->pid != 0);
+ int nregions = pstate->nregions;
+ region_type **regions = pstate->regions;
+
+ // Decrement the reference count on all the regions
+ for (int ii = 0; ii < nregions; ii++) {
+ if (regions[ii]->refs > 0) {
+ regions[ii]->refs -= 1;
+ continue;
+ }
+
+ delete regions[ii];
+ }
+ delete[] pstate->regions;
+ pstate->regions = NULL;
+ pstate->nregions = 0;
+ pstate->max_regions = 0;
+ pstate->addr_manager = pstate;
+ pstate->flags &= ~ProcessState::kIsClone;
+ pstate->flags &= ~ProcessState::kHasKernelRegion;
+ CopyKernelRegion(pstate);
+}
+
+template<class T>
+void TraceReader<T>::AddRegion(ProcessState *pstate, region_type *region)
+{
+ ProcessState *manager = pstate->addr_manager;
+ if (manager->regions == NULL) {
+ manager->max_regions = ProcessState::kInitialNumRegions;
+ manager->regions = new region_type*[manager->max_regions];
+ manager->nregions = 0;
+ }
+
+ // Check if we need to grow the array
+ int nregions = manager->nregions;
+ int max_regions = manager->max_regions;
+ if (nregions >= max_regions) {
+ max_regions <<= 1;
+ manager->max_regions = max_regions;
+ region_type **regions = new region_type*[max_regions];
+ for (int ii = 0; ii < nregions; ii++) {
+ regions[ii] = manager->regions[ii];
+ }
+ delete[] manager->regions;
+ manager->regions = regions;
+ }
+
+ // Add the new region to the end of the array and resort
+ manager->regions[nregions] = region;
+ nregions += 1;
+ manager->nregions = nregions;
+
+ // Resort the regions into increasing start address
+ qsort(manager->regions, nregions, sizeof(region_type*), cmp_region_addr<T>);
+}
+
+template<class T>
+void TraceReader<T>::CopyRegions(ProcessState *parent, ProcessState *child)
+{
+ // Copy the parent's address space
+ ProcessState *manager = parent->addr_manager;
+ int nregions = manager->nregions;
+ child->nregions = nregions;
+ child->max_regions = manager->max_regions;
+ region_type **regions = new region_type*[manager->max_regions];
+ child->regions = regions;
+ memcpy(regions, manager->regions, nregions * sizeof(region_type*));
+
+ // Increment the reference count on all the regions
+ for (int ii = 0; ii < nregions; ii++) {
+ regions[ii]->refs += 1;
+ }
+}
+
+template<class T>
+typename TraceReader<T>::region_type *
+TraceReader<T>::FindRegion(uint32_t addr, int nregions, region_type **regions)
+{
+ int high = nregions;
+ int low = -1;
+ while (low + 1 < high) {
+ int middle = (high + low) / 2;
+ uint32_t middle_addr = regions[middle]->vstart;
+ if (middle_addr == addr)
+ return regions[middle];
+ if (middle_addr > addr)
+ high = middle;
+ else
+ low = middle;
+ }
+
+ // If we get here then we did not find an exact address match. So use
+ // the closest region address that is less than the given address.
+ if (low < 0)
+ low = 0;
+ return regions[low];
+}
+
+template<class T>
+typename TraceReader<T>::symbol_type *
+TraceReader<T>::FindFunction(uint32_t addr, int nsyms, symbol_type *symbols,
+ bool exact_match)
+{
+ int high = nsyms;
+ int low = -1;
+ while (low + 1 < high) {
+ int middle = (high + low) / 2;
+ uint32_t middle_addr = symbols[middle].addr;
+ if (middle_addr == addr)
+ return &symbols[middle];
+ if (middle_addr > addr)
+ high = middle;
+ else
+ low = middle;
+ }
+
+ // If we get here then we did not find an exact address match. So use
+ // the closest function address that is less than the given address.
+ // We added a symbol with address zero so if there is no known
+ // function containing the given address, then we will return the
+ // "(unknown)" symbol.
+ if (low >= 0 && !exact_match)
+ return &symbols[low];
+ return NULL;
+}
+
+template<class T>
+typename TraceReader<T>::symbol_type *
+TraceReader<T>::LookupFunction(int pid, uint32_t addr, uint64_t time)
+{
+ // Check if the previous match is still a good match.
+ if (cached_pid_ == pid) {
+ uint32_t vstart = cached_func_->region->vstart;
+ uint32_t vend = cached_func_->region->vend;
+ if (addr >= vstart && addr < vend) {
+ uint32_t sym_addr = addr - cached_func_->region->base_addr;
+ if (sym_addr >= cached_func_->addr
+ && sym_addr < (cached_func_ + 1)->addr) {
+ // If this function is the virtual machine interpreter, then
+ // read the method trace to find the "real" method name based
+ // on the current time and pid.
+ if (cached_func_->flags & symbol_type::kIsInterpreter) {
+ symbol_type *sym = FindCurrentMethod(pid, time);
+ if (sym != NULL) {
+ sym->vm_sym = cached_func_;
+ return sym;
+ }
+ }
+ return cached_func_;
+ }
+ }
+ }
+
+ ProcessState *pstate = processes_[pid];
+ if (pstate == NULL) {
+ // There is no process state for the specified pid.
+ // This should never happen.
+ cached_pid_ = -1;
+ cached_func_ = NULL;
+ return NULL;
+ }
+ ProcessState *manager = pstate->addr_manager;
+ cached_pid_ = pid;
+ region_type *region = FindRegion(addr, manager->nregions, manager->regions);
+ uint32_t sym_addr = addr - region->base_addr;
+
+ cached_func_ = FindFunction(sym_addr, region->nsymbols, region->symbols,
+ false /* no exact match */);
+ if (cached_func_ != NULL) {
+ cached_func_->region = region;
+
+ // If this function is the virtual machine interpreter, then
+ // read the method trace to find the "real" method name based
+ // on the current time and pid.
+ if (cached_func_->flags & symbol_type::kIsInterpreter) {
+ symbol_type *sym = FindCurrentMethod(pid, time);
+ if (sym != NULL) {
+ sym->vm_sym = cached_func_;
+ return sym;
+ }
+ }
+ }
+
+ return cached_func_;
+}
+
+template <class T>
+void TraceReader<T>::HandlePidEvent(PidEvent *event)
+{
+ switch (event->rec_type) {
+ case kPidFork:
+ case kPidClone:
+ // event->pid is the process id of the child
+ if (event->pid >= kNumPids) {
+ fprintf(stderr, "Error: pid (%d) too large\n", event->pid);
+ exit(1);
+ }
+ // Create a new ProcessState struct for the child
+ // and link it in at the front of the list for that
+ // pid.
+ {
+ ProcessState *child = new ProcessState;
+ processes_[event->pid] = child;
+ child->pid = event->pid;
+ child->tgid = event->tgid;
+
+ // Link the new child at the front of the list (only needed if
+ // pids wrap around, which will probably never happen when
+ // tracing because it would take so long).
+ child->next = processes_[event->pid];
+ child->parent_pid = current_->pid;
+ child->parent = current_;
+ child->start_time = event->time;
+ child->name = Strdup(current_->name);
+ if (event->rec_type == kPidFork) {
+ CopyRegions(current_, child);
+ } else {
+ // Share the parent's address space
+ child->flags |= ProcessState::kIsClone;
+
+ // The address space manager for the clone is the same
+ // as the address space manager for the parent. This works
+ // even if the child later clones itself.
+ child->addr_manager = current_->addr_manager;
+ }
+ }
+ break;
+ case kPidSwitch:
+ // event->pid is the process id of the process we are
+ // switching to.
+ {
+ uint64_t elapsed = event->time - function_start_time_;
+ function_start_time_ = event->time;
+ current_->cpu_time += elapsed;
+ }
+ if (current_->flags & ProcessState::kCalledExit)
+ current_->end_time = event->time;
+
+ if (event->pid >= kNumPids) {
+ fprintf(stderr, "Error: pid (%d) too large\n", event->pid);
+ exit(1);
+ }
+
+ // If the process we are switching to does not exist, then
+ // create one. This can happen because the tracing code does
+ // not start tracing from the very beginning of the kernel.
+ current_ = processes_[event->pid];
+ if (current_ == NULL) {
+ current_ = new ProcessState;
+ processes_[event->pid] = current_;
+ current_->pid = event->pid;
+ current_->start_time = event->time;
+ CopyKernelRegion(current_);
+ }
+#if 0
+ {
+ printf("switching to p%d\n", current_->pid);
+ ProcessState *manager = current_->addr_manager;
+ for (int ii = 0; ii < manager->nregions; ++ii) {
+ printf(" %08x - %08x offset: %d nsyms: %4d %s\n",
+ manager->regions[ii]->vstart,
+ manager->regions[ii]->vend,
+ manager->regions[ii]->file_offset,
+ manager->regions[ii]->nsymbols,
+ manager->regions[ii]->path);
+ }
+ }
+#endif
+ break;
+ case kPidExit:
+ current_->exit_val = event->pid;
+ current_->flags |= ProcessState::kCalledExit;
+ break;
+ case kPidMmap:
+ {
+ region_type *region;
+ region_type *existing_region = hash_->Find(event->path);
+ if (existing_region == NULL || existing_region->vstart != event->vstart) {
+ // Create a new region and add it to the current process'
+ // address space.
+ region = new region_type;
+
+ // The event->path is allocated by ReadPidEvent() and owned
+ // by us.
+ region->path = event->path;
+ region->vstart = event->vstart;
+ region->vend = event->vend;
+ region->file_offset = event->offset;
+ if (existing_region == NULL) {
+ DexFileList *dexfile = dex_hash_->Find(event->path);
+ if (dexfile != NULL) {
+ PopulateSymbolsFromDexFile(dexfile, region);
+ } else {
+ ReadElfSymbols(region, 0);
+ }
+ hash_->Update(region->path, region);
+ } else {
+ region->nsymbols = existing_region->nsymbols;
+ region->symbols = existing_region->symbols;
+ region->path = existing_region->path;
+ delete[] event->path;
+ region->flags |= region_type::kSharedSymbols;
+ }
+
+ // The base_addr is subtracted from an address before the
+ // symbol name lookup and is either zero or event->vstart.
+ // HACK: Determine if base_addr is non-zero by looking at the
+ // second symbol address (skip the first symbol because that is
+ // the special symbol "(unknown)" with an address of zero).
+ if (region->nsymbols > 2 && region->symbols[1].addr < event->vstart)
+ region->base_addr = event->vstart;
+
+ // Treat all mmapped regions after the first as "libraries".
+ // Profiling tools can test for this property.
+ if (current_->flags & ProcessState::kHasFirstMmap)
+ region->flags |= region_type::kIsLibraryRegion;
+ else
+ current_->flags |= ProcessState::kHasFirstMmap;
+#if 0
+ printf("%s vstart: 0x%x vend: 0x%x offset: 0x%x\n",
+ region->path, region->vstart, region->vend, region->file_offset);
+#endif
+ } else {
+ region = existing_region;
+ region->refs += 1;
+ delete[] event->path;
+ }
+ AddRegion(current_, region);
+ }
+ break;
+ case kPidExec:
+ if (current_->argc > 0) {
+ for (int ii = 0; ii < current_->argc; ii++) {
+ delete[] current_->argv[ii];
+ }
+ delete[] current_->argv;
+ }
+ delete[] current_->name;
+
+ current_->argc = event->argc;
+ current_->argv = event->argv;
+ current_->name = Strdup(current_->argv[0]);
+ current_->flags |= ProcessState::kCalledExec;
+ ClearRegions(current_);
+ break;
+ case kPidName:
+ case kPidKthreadName:
+ {
+ ProcessState *pstate = processes_[event->pid];
+ if (pstate == NULL) {
+ pstate = new ProcessState;
+ if (event->rec_type == kPidKthreadName) {
+ pstate->tgid = event->tgid;
+ }
+ pstate->pid = event->pid;
+ pstate->start_time = event->time;
+ processes_[event->pid] = pstate;
+ CopyKernelRegion(pstate);
+ } else {
+ delete[] pstate->name;
+ }
+ pstate->name = event->path;
+ }
+ break;
+ case kPidNoAction:
+ break;
+ case kPidSymbolAdd:
+ delete[] event->path;
+ break;
+ case kPidSymbolRemove:
+ break;
+ }
+}
+
+// Finds the current pid for the given time. This routine reads the pid
+// trace file and assumes that the "time" parameter is monotonically
+// increasing.
+template <class T>
+int TraceReader<T>::FindCurrentPid(uint64_t time)
+{
+ if (time < next_pid_event_.time)
+ return current_->pid;
+
+ while (1) {
+ HandlePidEvent(&next_pid_event_);
+
+ if (internal_pid_reader_->ReadPidEvent(&next_pid_event_)) {
+ next_pid_event_.time = ~0ull;
+ break;
+ }
+ if (next_pid_event_.time > time)
+ break;
+ }
+ return current_->pid;
+}
+
+template <class T>
+void TraceReader<T>::ProcessState::DumpStack()
+{
+ for (int ii = 0; ii < method_stack_top; ii++) {
+ printf("%2d: 0x%08x\n", ii, method_stack[ii]);
+ }
+}
+
+template <class T>
+void TraceReader<T>::HandleMethodRecord(ProcessState *pstate,
+ MethodRec *method_rec)
+{
+ uint32_t addr;
+ int top = pstate->method_stack_top;
+ if (method_rec->flags == kMethodEnter) {
+ // Push this method on the stack
+ if (top >= pstate->kMaxMethodStackSize) {
+ fprintf(stderr, "Stack overflow at time %llu\n", method_rec->time);
+ exit(1);
+ }
+ pstate->method_stack[top] = method_rec->addr;
+ pstate->method_stack_top = top + 1;
+ addr = method_rec->addr;
+ } else {
+ if (top <= 0) {
+ // If the stack underflows, then set the current method to NULL.
+ pstate->current_method_sym = NULL;
+ return;
+ }
+ top -= 1;
+ addr = pstate->method_stack[top];
+ if (addr != method_rec->addr) {
+ fprintf(stderr,
+ "Stack method (0x%x) at index %d does not match trace record (0x%x) at time %llu\n",
+ addr, top, method_rec->addr, method_rec->time);
+ for (int ii = 0; ii <= top; ii++) {
+ fprintf(stderr, " %d: 0x%x\n", ii, pstate->method_stack[ii]);
+ }
+ exit(1);
+ }
+
+ pstate->method_stack_top = top;
+ if (top == 0) {
+ // When we empty the stack, set the current method to NULL
+ pstate->current_method_sym = NULL;
+ return;
+ }
+ addr = pstate->method_stack[top - 1];
+ }
+ ProcessState *manager = pstate->addr_manager;
+ region_type *region = FindRegion(addr, manager->nregions, manager->regions);
+ uint32_t sym_addr = addr - region->base_addr;
+ symbol_type *sym = FindFunction(sym_addr, region->nsymbols,
+ region->symbols, true /* exact match */);
+
+ pstate->current_method_sym = sym;
+ if (sym != NULL) {
+ sym->region = region;
+ }
+}
+
+template <class T>
+typename TraceReader<T>::symbol_type*
+TraceReader<T>::FindCurrentMethod(int pid, uint64_t time)
+{
+ ProcessState *procState = processes_[pid];
+
+ if (time < next_method_.time) {
+ return procState->current_method_sym;
+ }
+
+ while (1) {
+ if (next_method_.time != 0) {
+ // We may have to process methods from a different pid so use
+ // a local variable here so that we don't overwrite procState.
+ ProcessState *pState = processes_[next_method_.pid];
+ HandleMethodRecord(pState, &next_method_);
+ }
+
+ if (internal_method_reader_->ReadMethod(&next_method_)) {
+ next_method_.time = ~0ull;
+ break;
+ }
+ if (next_method_.time > time)
+ break;
+ }
+ return procState->current_method_sym;
+}
+
+template <class T>
+void TraceReader<T>::PopulateSymbolsFromDexFile(const DexFileList *dexfile,
+ region_type *region)
+
+{
+ int nsymbols = dexfile->nsymbols;
+ DexSym *dexsyms = dexfile->symbols;
+ region->nsymbols = nsymbols + 1;
+ symbol_type *symbols = new symbol_type[nsymbols + 1];
+ memset(symbols, 0, (nsymbols + 1) * sizeof(symbol_type));
+ region->symbols = symbols;
+ for (int ii = 0; ii < nsymbols; ii++) {
+ symbols[ii].addr = dexsyms[ii].addr;
+ symbols[ii].name = Strdup(dexsyms[ii].name);
+ symbols[ii].vm_sym = NULL;
+ symbols[ii].region = region;
+ symbols[ii].flags = symbol_type::kIsMethod;
+ }
+
+ // Add an entry at the end with an address of 0xffffffff. This
+ // is required for LookupFunction() to work.
+ symbol_type *symbol = &symbols[nsymbols];
+ symbol->addr = 0xffffffff;
+ symbol->name = Strdup("(end)");
+ symbol->vm_sym = NULL;
+ symbol->region = region;
+ symbol->flags = symbol_type::kIsMethod;
+}
+
+template <class T>
+bool TraceReader<T>::ReadMethodSymbol(MethodRec *method_record,
+ symbol_type **psym,
+ ProcessState **pproc)
+{
+ if (internal_method_reader_->ReadMethod(&next_method_)) {
+ return true;
+ }
+
+ // Copy the whole MethodRec struct
+ *method_record = next_method_;
+
+ uint64_t time = next_method_.time;
+
+ // Read the pid trace file up to this point to make sure the
+ // process state is valid.
+ FindCurrentPid(time);
+
+ ProcessState *pstate = processes_[next_method_.pid];
+ *pproc = pstate;
+ HandleMethodRecord(pstate, &next_method_);
+ *psym = pstate->current_method_sym;
+ return false;
+}
+
+#endif /* TRACE_READER_H */
diff --git a/emulator/qtools/trace_reader_base.h b/emulator/qtools/trace_reader_base.h
new file mode 100644
index 0000000..281d085
--- /dev/null
+++ b/emulator/qtools/trace_reader_base.h
@@ -0,0 +1,332 @@
+// Copyright 2006 The Android Open Source Project
+
+#ifndef TRACE_READER_BASE_H
+#define TRACE_READER_BASE_H
+
+#include <inttypes.h>
+#include "trace_common.h"
+#include "hash_table.h"
+
+class BBReader;
+class InsnReader;
+class AddrReader;
+class ExcReader;
+class PidReader;
+class MethodReader;
+
+struct StaticRec {
+ uint64_t bb_num;
+ uint32_t bb_addr;
+ uint32_t num_insns;
+};
+
+struct StaticBlock {
+ StaticRec rec;
+ uint32_t *insns;
+};
+
+struct BBEvent {
+ uint64_t time;
+ uint64_t bb_num;
+ uint32_t bb_addr;
+ uint32_t *insns;
+ int num_insns;
+ int pid;
+ int is_thumb;
+};
+
+struct PidEvent {
+ uint64_t time;
+ int rec_type; // record type: fork, context switch, exit ...
+ int tgid; // thread group id
+ int pid; // for fork: child pid; for switch: next pid;
+ // for exit: exit value
+ uint32_t vstart; // virtual start address (only used with mmap)
+ uint32_t vend; // virtual end address (only used with mmap)
+ uint32_t offset; // virtual file offset (only used with mmap)
+
+ // Dynamically allocated path to executable (or lib). In the case of
+ // an mmapped dex file, the path is modified to be more useful for
+ // comparing against the output of dexlist. For example, instead of this:
+ // /data/dalvik-cache/system@app@TestHarness.apk@classes.dex
+ // We convert to this:
+ // /system/app/TestHarness.apk
+ char *path;
+ char *mmap_path; // unmodified mmap path
+ int argc; // number of args
+ char **argv; // dynamically allocated array of args
+};
+
+struct MethodRec {
+ uint64_t time;
+ uint32_t addr;
+ int pid;
+ int flags;
+};
+
+struct DexSym {
+ uint32_t addr;
+ int len;
+ char *name;
+};
+
+struct DexFileList {
+ char *path;
+ int nsymbols;
+ DexSym *symbols;
+};
+
+class TraceReaderBase {
+ public:
+ TraceReaderBase();
+ virtual ~TraceReaderBase();
+
+ friend class BBReader;
+
+ void Open(char *filename);
+ void Close();
+ void WriteHeader(TraceHeader *header);
+ inline bool ReadBB(BBEvent *event);
+ int ReadStatic(StaticRec *rec);
+ int ReadStaticInsns(int num, uint32_t *insns);
+ TraceHeader *GetHeader() { return header_; }
+ inline uint64_t ReadInsnTime(uint64_t min_time);
+ void TruncateLastBlock(uint32_t num_insns);
+ inline bool ReadAddr(uint64_t *time, uint32_t *addr, int *flags);
+ inline bool ReadExc(uint64_t *time, uint32_t *current_pc,
+ uint64_t *recnum, uint32_t *target_pc,
+ uint64_t *bb_num, uint64_t *bb_start_time,
+ int *num_insns);
+ inline bool ReadPidEvent(PidEvent *event);
+ inline bool ReadMethod(MethodRec *method_record);
+ StaticBlock *GetStaticBlock(uint64_t bb_num) { return &blocks_[bb_num]; }
+ uint32_t *GetInsns(uint64_t bb_num) { return blocks_[bb_num].insns; }
+ uint32_t GetBBAddr(uint64_t bb_num) {
+ return blocks_[bb_num].rec.bb_addr & ~1;
+ }
+ int GetIsThumb(uint64_t bb_num) {
+ return blocks_[bb_num].rec.bb_addr & 1;
+ }
+ void SetPostProcessing(bool val) { post_processing_ = val; }
+
+ protected:
+ virtual int FindCurrentPid(uint64_t time);
+ int current_pid_;
+ int next_pid_;
+ uint64_t next_pid_switch_time_;
+ PidReader *internal_pid_reader_;
+ MethodReader *internal_method_reader_;
+ HashTable<DexFileList*> *dex_hash_;
+
+ private:
+ int FindNumInsns(uint64_t bb_num, uint64_t bb_start_time);
+ void ReadTraceHeader(FILE *fstream, char *filename,
+ char *tracename, TraceHeader *header);
+ PidEvent *FindMmapDexFileEvent();
+ void ParseDexList(char *filename);
+
+ char *static_filename_;
+ FILE *static_fstream_;
+ TraceHeader *header_;
+ BBReader *bb_reader_;
+ InsnReader *insn_reader_;
+ AddrReader *load_addr_reader_;
+ AddrReader *store_addr_reader_;
+ ExcReader *exc_reader_;
+ PidReader *pid_reader_;
+ MethodReader *method_reader_;
+ ExcReader *internal_exc_reader_;
+ StaticBlock *blocks_;
+ bool exc_end_;
+ uint64_t bb_recnum_;
+ uint64_t exc_recnum_;
+ uint64_t exc_bb_num_;
+ uint64_t exc_time_;
+ int exc_num_insns_;
+ bool post_processing_;
+
+ bool load_eof_;
+ uint64_t load_time_;
+ uint32_t load_addr_;
+ bool store_eof_;
+ uint64_t store_time_;
+ uint32_t store_addr_;
+};
+
+class Decoder;
+
+class BBReader {
+ public:
+ explicit BBReader(TraceReaderBase *trace);
+ ~BBReader();
+ void Open(char *filename);
+ void Close();
+ bool ReadBB(BBEvent *event);
+
+ private:
+ struct TimeRec {
+ BBRec bb_rec;
+ uint64_t next_time;
+ };
+
+ struct Future {
+ Future *next;
+ TimeRec bb;
+ };
+
+ inline Future *AllocFuture();
+ inline void FreeFuture(Future *future);
+ inline void InsertFuture(Future *future);
+ inline int DecodeNextRec();
+
+ TimeRec nextrec_;
+ Future futures_[kMaxNumBasicBlocks];
+ Future *head_;
+ Future *free_;
+ Decoder *decoder_;
+ bool is_eof_;
+ TraceReaderBase *trace_;
+};
+
+class InsnReader {
+ public:
+ InsnReader();
+ ~InsnReader();
+
+ void Open(char *filename);
+ void Close();
+ uint64_t ReadInsnTime(uint64_t min_time);
+
+ private:
+ Decoder *decoder_;
+ uint64_t prev_time_;
+ uint64_t time_diff_;
+ int repeat_;
+};
+
+class AddrReader {
+ public:
+ AddrReader();
+ ~AddrReader();
+
+ bool Open(char *filename, char *suffix);
+ void Close();
+ bool ReadAddr(uint64_t *time, uint32_t *addr);
+
+ private:
+ Decoder *decoder_;
+ uint32_t prev_addr_;
+ uint64_t prev_time_;
+ bool opened_; // true after file is opened
+};
+
+class ExcReader {
+ public:
+ ExcReader();
+ ~ExcReader();
+
+ void Open(char *filename);
+ void Close();
+ bool ReadExc(uint64_t *time, uint32_t *current_pc,
+ uint64_t *recnum, uint32_t *target_pc,
+ uint64_t *bb_num, uint64_t *bb_start_time,
+ int *num_insns);
+
+ private:
+ Decoder *decoder_;
+ uint64_t prev_time_;
+ uint64_t prev_recnum_;
+};
+
+class PidReader {
+ public:
+ PidReader();
+ ~PidReader();
+
+ void Open(char *filename);
+ void Close();
+ bool ReadPidEvent(struct PidEvent *event);
+ void Dispose(struct PidEvent *event);
+
+ private:
+ Decoder *decoder_;
+ uint64_t prev_time_;
+};
+
+class MethodReader {
+ public:
+ MethodReader();
+ ~MethodReader();
+
+ bool Open(char *filename);
+ void Close();
+ bool ReadMethod(MethodRec *method_record);
+
+ private:
+ Decoder *decoder_;
+ uint64_t prev_time_;
+ uint32_t prev_addr_;
+ int32_t prev_pid_;
+ bool opened_; // true after file is opened
+};
+
+// Reads the next dynamic basic block from the trace.
+// Returns true on end-of-file.
+inline bool TraceReaderBase::ReadBB(BBEvent *event)
+{
+ bb_recnum_ += 1;
+ return bb_reader_->ReadBB(event);
+}
+
+inline uint64_t TraceReaderBase::ReadInsnTime(uint64_t min_time)
+{
+ return insn_reader_->ReadInsnTime(min_time);
+}
+
+inline bool TraceReaderBase::ReadAddr(uint64_t *time, uint32_t *addr, int *flags)
+{
+ if (load_eof_ && store_eof_)
+ return true;
+
+ if (store_eof_ || (!load_eof_ && load_time_ <= store_time_)) {
+ *time = load_time_;
+ *addr = load_addr_;
+ *flags = 0;
+ load_eof_ = load_addr_reader_->ReadAddr(&load_time_, &load_addr_);
+ } else {
+ *time = store_time_;
+ *addr = store_addr_;
+ *flags = 1;
+ store_eof_ = store_addr_reader_->ReadAddr(&store_time_, &store_addr_);
+ }
+ return false;
+}
+
+inline bool TraceReaderBase::ReadExc(uint64_t *time, uint32_t *current_pc,
+ uint64_t *recnum, uint32_t *target_pc,
+ uint64_t *bb_num, uint64_t *bb_start_time,
+ int *num_insns)
+{
+ return exc_reader_->ReadExc(time, current_pc, recnum, target_pc, bb_num,
+ bb_start_time, num_insns);
+}
+
+inline bool TraceReaderBase::ReadPidEvent(PidEvent *event)
+{
+ return pid_reader_->ReadPidEvent(event);
+}
+
+inline bool TraceReaderBase::ReadMethod(MethodRec *method_record)
+{
+ return method_reader_->ReadMethod(method_record);
+}
+
+// Duplicates a string, allocating space using new[].
+inline char * Strdup(const char *src) {
+ int len = strlen(src);
+ char *copy = new char[len + 1];
+ strcpy(copy, src);
+ return copy;
+}
+
+#endif /* TRACE_READER_BASE_H */