resolved conflicts for merge of 19cb54ea to eclair
Change-Id: I182195aeaad88aebc82988b1cec9ebda0c359bc8
@@ -18,10 +18,10 @@
|
||||
# These are the files that comprise that SDK
|
||||
#
|
||||
|
||||
# version files for the SDK updater
|
||||
development/tools/scripts/doc_source.properties docs/source.properties
|
||||
development/tools/scripts/tools_source.properties tools/source.properties
|
||||
development/tools/scripts/platform_source.properties platforms/${PLATFORM_NAME}/source.properties
|
||||
# version files for the SDK updater, from sdk.git
|
||||
sdk/scripts/doc_source.properties docs/source.properties
|
||||
sdk/scripts/tools_source.properties tools/source.properties
|
||||
sdk/scripts/platform_source.properties platforms/${PLATFORM_NAME}/source.properties
|
||||
|
||||
# host tools from out/host/$(HOST_OS)-$(HOST_ARCH)/
|
||||
bin/aapt platforms/${PLATFORM_NAME}/tools/aapt
|
||||
@@ -42,29 +42,32 @@ framework/org.eclipse.equinox.common_3.4.0.v20080421-2006.jar tools/lib/org.ecli
|
||||
framework/org.eclipse.jface_3.4.2.M20090107-0800.jar tools/lib/org.eclipse.jface_3.4.2.M20090107-0800.jar
|
||||
framework/osgi.jar tools/lib/osgi.jar
|
||||
|
||||
# copy build prop from out/.../sdk/
|
||||
sdk/sdk-build.prop platforms/${PLATFORM_NAME}/build.prop
|
||||
development/tools/scripts/plugin.prop tools/lib/plugin.prop
|
||||
|
||||
# copy plugin.prop from sdk.git
|
||||
sdk/scripts/plugin.prop tools/lib/plugin.prop
|
||||
|
||||
# the aidl precompiled include
|
||||
obj/framework.aidl platforms/${PLATFORM_NAME}/framework.aidl
|
||||
|
||||
# sdk scripts
|
||||
development/tools/scripts/AndroidManifest.template platforms/${PLATFORM_NAME}/templates/AndroidManifest.template
|
||||
development/tools/scripts/AndroidManifest.tests.template platforms/${PLATFORM_NAME}/templates/AndroidManifest.tests.template
|
||||
development/tools/scripts/java_file.template platforms/${PLATFORM_NAME}/templates/java_file.template
|
||||
development/tools/scripts/java_tests_file.template platforms/${PLATFORM_NAME}/templates/java_tests_file.template
|
||||
development/tools/scripts/layout.template platforms/${PLATFORM_NAME}/templates/layout.template
|
||||
development/tools/scripts/strings.template platforms/${PLATFORM_NAME}/templates/strings.template
|
||||
development/tools/scripts/android_rules.xml platforms/${PLATFORM_NAME}/templates/android_rules.xml
|
||||
development/tools/scripts/android_test_rules.xml platforms/${PLATFORM_NAME}/templates/android_test_rules.xml
|
||||
development/tools/scripts/icon_ldpi.png platforms/${PLATFORM_NAME}/templates/icon_ldpi.png
|
||||
development/tools/scripts/icon_mdpi.png platforms/${PLATFORM_NAME}/templates/icon_mdpi.png
|
||||
development/tools/scripts/icon_hdpi.png platforms/${PLATFORM_NAME}/templates/icon_hdpi.png
|
||||
development/tools/scripts/build.template tools/lib/build.template
|
||||
development/tools/scripts/devices.xml tools/lib/devices.xml
|
||||
# sdk.git scripts
|
||||
sdk/scripts/AndroidManifest.template platforms/${PLATFORM_NAME}/templates/AndroidManifest.template
|
||||
sdk/scripts/AndroidManifest.tests.template platforms/${PLATFORM_NAME}/templates/AndroidManifest.tests.template
|
||||
sdk/scripts/java_file.template platforms/${PLATFORM_NAME}/templates/java_file.template
|
||||
sdk/scripts/java_tests_file.template platforms/${PLATFORM_NAME}/templates/java_tests_file.template
|
||||
sdk/scripts/layout.template platforms/${PLATFORM_NAME}/templates/layout.template
|
||||
sdk/scripts/strings.template platforms/${PLATFORM_NAME}/templates/strings.template
|
||||
sdk/scripts/android_rules.xml platforms/${PLATFORM_NAME}/templates/android_rules.xml
|
||||
sdk/scripts/android_test_rules.xml platforms/${PLATFORM_NAME}/templates/android_test_rules.xml
|
||||
sdk/scripts/icon_ldpi.png platforms/${PLATFORM_NAME}/templates/icon_ldpi.png
|
||||
sdk/scripts/icon_mdpi.png platforms/${PLATFORM_NAME}/templates/icon_mdpi.png
|
||||
sdk/scripts/icon_hdpi.png platforms/${PLATFORM_NAME}/templates/icon_hdpi.png
|
||||
sdk/scripts/build.template tools/lib/build.template
|
||||
sdk/scripts/devices.xml tools/lib/devices.xml
|
||||
|
||||
# emacs support
|
||||
development/tools/scripts/android.el tools/lib/android.el
|
||||
# emacs support from sdk.git
|
||||
sdk/scripts/android.el tools/lib/android.el
|
||||
|
||||
# samples
|
||||
development/apps/GestureBuilder platforms/${PLATFORM_NAME}/samples/GestureBuilder
|
||||
@@ -156,20 +159,20 @@ userdata.img platforms/${PLATFORM_NAME}/images/userdata.img
|
||||
prebuilt/android-arm/kernel/kernel-qemu platforms/${PLATFORM_NAME}/images/kernel-qemu
|
||||
external/qemu/android/avd/hardware-properties.ini tools/lib/hardware-properties.ini
|
||||
|
||||
# emulator skins
|
||||
development/emulator/skins/QVGA platforms/${PLATFORM_NAME}/skins/QVGA
|
||||
development/emulator/skins/WQVGA432 platforms/${PLATFORM_NAME}/skins/WQVGA432
|
||||
development/emulator/skins/WQVGA400 platforms/${PLATFORM_NAME}/skins/WQVGA400
|
||||
development/emulator/skins/HVGA platforms/${PLATFORM_NAME}/skins/HVGA
|
||||
development/emulator/skins/WVGA800 platforms/${PLATFORM_NAME}/skins/WVGA800
|
||||
development/emulator/skins/WVGA854 platforms/${PLATFORM_NAME}/skins/WVGA854
|
||||
# emulator skins from sdk.git
|
||||
sdk/emulator/skins/QVGA platforms/${PLATFORM_NAME}/skins/QVGA
|
||||
sdk/emulator/skins/WQVGA432 platforms/${PLATFORM_NAME}/skins/WQVGA432
|
||||
sdk/emulator/skins/WQVGA400 platforms/${PLATFORM_NAME}/skins/WQVGA400
|
||||
sdk/emulator/skins/HVGA platforms/${PLATFORM_NAME}/skins/HVGA
|
||||
sdk/emulator/skins/WVGA800 platforms/${PLATFORM_NAME}/skins/WVGA800
|
||||
sdk/emulator/skins/WVGA854 platforms/${PLATFORM_NAME}/skins/WVGA854
|
||||
|
||||
# NOTICE files are copied by build/core/Makefile
|
||||
development/tools/scripts/sdk_files_NOTICE.txt platforms/${PLATFORM_NAME}/templates/NOTICE.txt
|
||||
development/tools/scripts/sdk_files_NOTICE.txt platforms/${PLATFORM_NAME}/samples/NOTICE.txt
|
||||
development/tools/scripts/sdk_files_NOTICE.txt platforms/${PLATFORM_NAME}/data/NOTICE.txt
|
||||
development/tools/scripts/sdk_files_NOTICE.txt platforms/${PLATFORM_NAME}/skins/NOTICE.txt
|
||||
development/tools/scripts/sdk_files_NOTICE.txt platforms/${PLATFORM_NAME}/tools/NOTICE.txt
|
||||
# NOTICE files are copied by build/core/Makefile from sdk.git
|
||||
sdk/scripts/sdk_files_NOTICE.txt platforms/${PLATFORM_NAME}/templates/NOTICE.txt
|
||||
sdk/scripts/sdk_files_NOTICE.txt platforms/${PLATFORM_NAME}/samples/NOTICE.txt
|
||||
sdk/scripts/sdk_files_NOTICE.txt platforms/${PLATFORM_NAME}/data/NOTICE.txt
|
||||
sdk/scripts/sdk_files_NOTICE.txt platforms/${PLATFORM_NAME}/skins/NOTICE.txt
|
||||
sdk/scripts/sdk_files_NOTICE.txt platforms/${PLATFORM_NAME}/tools/NOTICE.txt
|
||||
|
||||
# the readme
|
||||
development/docs/SDK_RELEASE_NOTES RELEASE_NOTES.html
|
||||
@@ -202,5 +205,5 @@ frameworks/base/data/fonts/DroidSerif-Regular.ttf platforms/${PLATFORM_NAME}/
|
||||
frameworks/base/data/fonts/DroidSansFallback.ttf platforms/${PLATFORM_NAME}/data/fonts/DroidSansFallback.ttf
|
||||
frameworks/base/data/fonts/DroidSansJapanese.ttf platforms/${PLATFORM_NAME}/data/fonts/DroidSansJapanese.ttf
|
||||
|
||||
# empty add-on folder with just a readme
|
||||
development/tools/scripts/README_add-ons.txt add-ons/README.txt
|
||||
# empty add-on folder with just a readme copied from sdk.git
|
||||
sdk/scripts/README_add-ons.txt add-ons/README.txt
|
||||
|
||||
@@ -163,13 +163,13 @@ function package() {
|
||||
cp -v /cygdrive/c/cygwin/bin/mgwz.dll "$TOOLS"/
|
||||
|
||||
# Update a bunch of bat files
|
||||
cp -v development/tools/apkbuilder/etc/apkbuilder.bat "$TOOLS"/
|
||||
cp -v development/tools/ddms/app/etc/ddms.bat "$TOOLS"/
|
||||
cp -v development/tools/traceview/etc/traceview.bat "$TOOLS"/
|
||||
cp -v development/tools/hierarchyviewer/etc/hierarchyviewer.bat "$TOOLS"/
|
||||
cp -v development/tools/layoutopt/app/etc/layoutopt.bat "$TOOLS"/
|
||||
cp -v development/tools/draw9patch/etc/draw9patch.bat "$TOOLS"/
|
||||
cp -v development/tools/sdkmanager/app/etc/android.bat "$TOOLS"/
|
||||
cp -v sdk/apkbuilder/etc/apkbuilder.bat "$TOOLS"/
|
||||
cp -v sdk/ddms/app/etc/ddms.bat "$TOOLS"/
|
||||
cp -v sdk/traceview/etc/traceview.bat "$TOOLS"/
|
||||
cp -v sdk/hierarchyviewer/etc/hierarchyviewer.bat "$TOOLS"/
|
||||
cp -v sdk/layoutopt/app/etc/layoutopt.bat "$TOOLS"/
|
||||
cp -v sdk/draw9patch/etc/draw9patch.bat "$TOOLS"/
|
||||
cp -v sdk/sdkmanager/app/etc/android.bat "$TOOLS"/
|
||||
|
||||
# Put the JetCreator tools, content and docs (not available in the linux SDK)
|
||||
JET="$TOOLS/Jet"
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
key 200 MEDIA_PLAY_PAUSE WAKE
|
||||
key 201 MEDIA_PLAY_PAUSE WAKE
|
||||
key 166 MEDIA_STOP WAKE
|
||||
key 163 MEDIA_NEXT WAKE
|
||||
key 165 MEDIA_PREVIOUS WAKE
|
||||
key 168 MEDIA_REWIND WAKE
|
||||
key 208 MEDIA_FAST_FORWARD WAKE
|
||||
@@ -1,18 +0,0 @@
|
||||
LOCAL_PATH:= $(call my-dir)
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_SRC_FILES := qwerty.kcm
|
||||
include $(BUILD_KEY_CHAR_MAP)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_SRC_FILES := qwerty2.kcm
|
||||
include $(BUILD_KEY_CHAR_MAP)
|
||||
|
||||
file := $(TARGET_OUT_KEYLAYOUT)/qwerty.kl
|
||||
ALL_PREBUILT += $(file)
|
||||
$(file): $(LOCAL_PATH)/qwerty.kl | $(ACP)
|
||||
$(transform-prebuilt-to-target)
|
||||
|
||||
file := $(TARGET_OUT_KEYLAYOUT)/AVRCP.kl
|
||||
ALL_PREBUILT += $(file)
|
||||
$(file) : $(LOCAL_PATH)/AVRCP.kl | $(ACP)
|
||||
$(transform-prebuilt-to-target)
|
||||
@@ -1,64 +0,0 @@
|
||||
[type=QWERTY]
|
||||
|
||||
# keycode display number base caps fn caps_fn
|
||||
|
||||
A 'A' '2' 'a' 'A' '#' 0x00
|
||||
B 'B' '2' 'b' 'B' '<' 0x00
|
||||
C 'C' '2' 'c' 'C' '9' 0x00E7
|
||||
D 'D' '3' 'd' 'D' '5' 0x00
|
||||
E 'E' '3' 'e' 'E' '2' 0x0301
|
||||
F 'F' '3' 'f' 'F' '6' 0x00A5
|
||||
G 'G' '4' 'g' 'G' '-' '_'
|
||||
H 'H' '4' 'h' 'H' '[' '{'
|
||||
I 'I' '4' 'i' 'I' '$' 0x0302
|
||||
J 'J' '5' 'j' 'J' ']' '}'
|
||||
K 'K' '5' 'k' 'K' '"' '~'
|
||||
L 'L' '5' 'l' 'L' ''' '`'
|
||||
M 'M' '6' 'm' 'M' '!' 0x00
|
||||
N 'N' '6' 'n' 'N' '>' 0x0303
|
||||
O 'O' '6' 'o' 'O' '(' 0x00
|
||||
P 'P' '7' 'p' 'P' ')' 0x00
|
||||
Q 'Q' '7' 'q' 'Q' '*' 0x0300
|
||||
R 'R' '7' 'r' 'R' '3' 0x20AC
|
||||
S 'S' '7' 's' 'S' '4' 0x00DF
|
||||
T 'T' '8' 't' 'T' '+' 0x00A3
|
||||
U 'U' '8' 'u' 'U' '&' 0x0308
|
||||
V 'V' '8' 'v' 'V' '=' '^'
|
||||
W 'W' '9' 'w' 'W' '1' 0x00
|
||||
X 'X' '9' 'x' 'X' '8' 0xEF00
|
||||
Y 'Y' '9' 'y' 'Y' '%' 0x00A1
|
||||
Z 'Z' '9' 'z' 'Z' '7' 0x00
|
||||
|
||||
# on pc keyboards
|
||||
COMMA ',' ',' ',' ';' ';' '|'
|
||||
PERIOD '.' '.' '.' ':' ':' 0x2026
|
||||
AT '@' '0' '@' '0' '0' 0x2022
|
||||
SLASH '/' '/' '/' '?' '?' '\'
|
||||
|
||||
SPACE 0x20 0x20 0x20 0x20 0xEF01 0xEF01
|
||||
ENTER 0xa 0xa 0xa 0xa 0xa 0xa
|
||||
|
||||
TAB 0x9 0x9 0x9 0x9 0x9 0x9
|
||||
0 '0' '0' '0' ')' ')' ')'
|
||||
1 '1' '1' '1' '!' '!' '!'
|
||||
2 '2' '2' '2' '@' '@' '@'
|
||||
3 '3' '3' '3' '#' '#' '#'
|
||||
4 '4' '4' '4' '$' '$' '$'
|
||||
5 '5' '5' '5' '%' '%' '%'
|
||||
6 '6' '6' '6' '^' '^' '^'
|
||||
7 '7' '7' '7' '&' '&' '&'
|
||||
8 '8' '8' '8' '*' '*' '*'
|
||||
9 '9' '9' '9' '(' '(' '('
|
||||
|
||||
GRAVE '`' '`' '`' '~' '`' '~'
|
||||
MINUS '-' '-' '-' '_' '-' '_'
|
||||
EQUALS '=' '=' '=' '+' '=' '+'
|
||||
LEFT_BRACKET '[' '[' '[' '{' '[' '{'
|
||||
RIGHT_BRACKET ']' ']' ']' '}' ']' '}'
|
||||
BACKSLASH '\' '\' '\' '|' '\' '|'
|
||||
SEMICOLON ';' ';' ';' ':' ';' ':'
|
||||
APOSTROPHE ''' ''' ''' '"' ''' '"'
|
||||
STAR '*' '*' '*' '*' '*' '*'
|
||||
POUND '#' '#' '#' '#' '#' '#'
|
||||
PLUS '+' '+' '+' '+' '+' '+'
|
||||
|
||||
@@ -1,89 +0,0 @@
|
||||
key 399 GRAVE
|
||||
key 2 1
|
||||
key 3 2
|
||||
key 4 3
|
||||
key 5 4
|
||||
key 6 5
|
||||
key 7 6
|
||||
key 8 7
|
||||
key 9 8
|
||||
key 10 9
|
||||
key 11 0
|
||||
key 158 BACK WAKE_DROPPED
|
||||
key 230 SOFT_RIGHT WAKE
|
||||
key 60 SOFT_RIGHT WAKE
|
||||
key 107 ENDCALL WAKE_DROPPED
|
||||
key 62 ENDCALL WAKE_DROPPED
|
||||
key 229 MENU WAKE_DROPPED
|
||||
key 139 MENU WAKE_DROPPED
|
||||
key 59 MENU WAKE_DROPPED
|
||||
key 127 SEARCH WAKE_DROPPED
|
||||
key 217 SEARCH WAKE_DROPPED
|
||||
key 228 POUND
|
||||
key 227 STAR
|
||||
key 231 CALL WAKE_DROPPED
|
||||
key 61 CALL WAKE_DROPPED
|
||||
key 232 DPAD_CENTER WAKE_DROPPED
|
||||
key 108 DPAD_DOWN WAKE_DROPPED
|
||||
key 103 DPAD_UP WAKE_DROPPED
|
||||
key 102 HOME WAKE
|
||||
key 105 DPAD_LEFT WAKE_DROPPED
|
||||
key 106 DPAD_RIGHT WAKE_DROPPED
|
||||
key 115 VOLUME_UP
|
||||
key 114 VOLUME_DOWN
|
||||
key 116 POWER WAKE
|
||||
key 212 CAMERA
|
||||
|
||||
key 16 Q
|
||||
key 17 W
|
||||
key 18 E
|
||||
key 19 R
|
||||
key 20 T
|
||||
key 21 Y
|
||||
key 22 U
|
||||
key 23 I
|
||||
key 24 O
|
||||
key 25 P
|
||||
key 26 LEFT_BRACKET
|
||||
key 27 RIGHT_BRACKET
|
||||
key 43 BACKSLASH
|
||||
|
||||
key 30 A
|
||||
key 31 S
|
||||
key 32 D
|
||||
key 33 F
|
||||
key 34 G
|
||||
key 35 H
|
||||
key 36 J
|
||||
key 37 K
|
||||
key 38 L
|
||||
key 39 SEMICOLON
|
||||
key 40 APOSTROPHE
|
||||
key 14 DEL
|
||||
|
||||
key 44 Z
|
||||
key 45 X
|
||||
key 46 C
|
||||
key 47 V
|
||||
key 48 B
|
||||
key 49 N
|
||||
key 50 M
|
||||
key 51 COMMA
|
||||
key 52 PERIOD
|
||||
key 53 SLASH
|
||||
key 28 ENTER
|
||||
|
||||
key 56 ALT_LEFT
|
||||
key 100 ALT_RIGHT
|
||||
key 42 SHIFT_LEFT
|
||||
key 54 SHIFT_RIGHT
|
||||
key 15 TAB
|
||||
key 57 SPACE
|
||||
key 150 EXPLORER
|
||||
key 155 ENVELOPE
|
||||
|
||||
key 12 MINUS
|
||||
key 13 EQUALS
|
||||
key 215 AT
|
||||
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
[type=QWERTY]
|
||||
|
||||
# this keymap is to be used in the emulator only. note
|
||||
# that I have liberally modified certain key strokes to
|
||||
# make it more usable in this context. the main differences
|
||||
# with the reference keyboard image are:
|
||||
#
|
||||
# - cap-2 produces '@', and not '"', without that, typing
|
||||
# email addresses becomes a major pain with a qwerty
|
||||
# keyboard. note that you can type '"' with fn-E anyway.
|
||||
#
|
||||
# - cap-COMMA and cap-PERIOD return '<' and '>', instead
|
||||
# of nothing.
|
||||
#
|
||||
#
|
||||
|
||||
# keycode display number base caps fn caps_fn
|
||||
|
||||
A 'A' '2' 'a' 'A' 'a' 'A'
|
||||
B 'B' '2' 'b' 'B' 'b' 'B'
|
||||
C 'C' '2' 'c' 'C' 0x00e7 0x00E7
|
||||
D 'D' '3' 'd' 'D' ''' '''
|
||||
E 'E' '3' 'e' 'E' '"' 0x0301
|
||||
F 'F' '3' 'f' 'F' '[' '['
|
||||
G 'G' '4' 'g' 'G' ']' ']'
|
||||
H 'H' '4' 'h' 'H' '<' '<'
|
||||
I 'I' '4' 'i' 'I' '-' 0x0302
|
||||
J 'J' '5' 'j' 'J' '>' '>'
|
||||
K 'K' '5' 'k' 'K' ';' '~'
|
||||
L 'L' '5' 'l' 'L' ':' '`'
|
||||
M 'M' '6' 'm' 'M' '%' 0x00
|
||||
N 'N' '6' 'n' 'N' 0x00 0x0303
|
||||
O 'O' '6' 'o' 'O' '+' '+'
|
||||
P 'P' '7' 'p' 'P' '=' 0x00A5
|
||||
Q 'Q' '7' 'q' 'Q' '|' 0x0300
|
||||
R 'R' '7' 'r' 'R' '`' 0x20AC
|
||||
S 'S' '7' 's' 'S' '\' 0x00DF
|
||||
T 'T' '8' 't' 'T' '{' 0x00A3
|
||||
U 'U' '8' 'u' 'U' '_' 0x0308
|
||||
V 'V' '8' 'v' 'V' 'v' 'V'
|
||||
W 'W' '9' 'w' 'W' '~' '~'
|
||||
X 'X' '9' 'x' 'X' 'x' 0xEF00
|
||||
Y 'Y' '9' 'y' 'Y' '}' 0x00A1
|
||||
Z 'Z' '9' 'z' 'Z' 'z' 'Z'
|
||||
|
||||
COMMA ',' ',' ',' '<' ',' ','
|
||||
PERIOD '.' '.' '.' '>' '.' 0x2026
|
||||
AT '@' '@' '@' '@' '@' 0x2022
|
||||
SLASH '/' '/' '/' '?' '?' '?'
|
||||
|
||||
SPACE 0x20 0x20 0x20 0x20 0xEF01 0xEF01
|
||||
ENTER 0xa 0xa 0xa 0xa 0xa 0xa
|
||||
|
||||
0 '0' '0' '0' ')' ')' ')'
|
||||
1 '1' '1' '1' '!' '!' '!'
|
||||
2 '2' '2' '2' '@' '@' '@'
|
||||
3 '3' '3' '3' '#' '#' '#'
|
||||
4 '4' '4' '4' '$' '$' '$'
|
||||
5 '5' '5' '5' '%' '%' '%'
|
||||
6 '6' '6' '6' '^' '^' '^'
|
||||
7 '7' '7' '7' '&' '&' '&'
|
||||
8 '8' '8' '8' '*' '*' '*'
|
||||
9 '9' '9' '9' '(' '(' '('
|
||||
|
||||
# the rest is for a qwerty keyboard
|
||||
#
|
||||
TAB 0x9 0x9 0x9 0x9 0x9 0x9
|
||||
GRAVE '`' '`' '`' '~' '`' '~'
|
||||
MINUS '-' '-' '-' '_' '-' '_'
|
||||
EQUALS '=' '=' '=' '+' '=' '+'
|
||||
LEFT_BRACKET '[' '[' '[' '{' '[' '{'
|
||||
RIGHT_BRACKET ']' ']' ']' '}' ']' '}'
|
||||
BACKSLASH '\' '\' '\' '|' '\' '|'
|
||||
SEMICOLON ';' ';' ';' ':' ';' ':'
|
||||
APOSTROPHE ''' ''' ''' '"' ''' '"'
|
||||
STAR '*' '*' '*' '*' '*' '*'
|
||||
POUND '#' '#' '#' '#' '#' '#'
|
||||
PLUS '+' '+' '+' '+' '+' '+'
|
||||
|
||||
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
# Copyright 2006 The Android Open Source Project
|
||||
|
||||
LOCAL_PATH:= $(call my-dir)
|
||||
|
||||
# host executable
|
||||
#
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_SRC_FILES:= mksdcard.c
|
||||
LOCAL_MODULE = mksdcard
|
||||
include $(BUILD_HOST_EXECUTABLE)
|
||||
|
||||
@@ -1,304 +0,0 @@
|
||||
/* mksdcard.c
|
||||
**
|
||||
** Copyright 2007, The Android Open Source Project
|
||||
**
|
||||
** Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions are met:
|
||||
** * Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions and the following disclaimer.
|
||||
** * Redistributions in binary form must reproduce the above copyright
|
||||
** notice, this list of conditions and the following disclaimer in the
|
||||
** documentation and/or other materials provided with the distribution.
|
||||
** * Neither the name of Google Inc. nor the names of its contributors may
|
||||
** be used to endorse or promote products derived from this software
|
||||
** without specific prior written permission.
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR
|
||||
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
** EVENT SHALL Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/* a simple and portable program used to generate a blank FAT32 image file
|
||||
*
|
||||
* usage: mksdcard [-l label] <size> <filename>
|
||||
*/
|
||||
|
||||
#include <time.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/* believe me, you *don't* want to change these constants !! */
|
||||
#define BYTES_PER_SECTOR 512
|
||||
#define RESERVED_SECTORS 32
|
||||
#define BACKUP_BOOT_SECTOR 6
|
||||
#define NUM_FATS 2
|
||||
|
||||
typedef long long Wide; /* might be something else if you don't use GCC */
|
||||
typedef unsigned char Byte;
|
||||
typedef Byte* Bytes;
|
||||
|
||||
#define BYTE_(p,i) (((Bytes)(p))[(i)])
|
||||
|
||||
#define POKEB(p,v) BYTE_(p,0) = (Byte)(v)
|
||||
#define POKES(p,v) ( BYTE_(p,0) = (Byte)(v), BYTE_(p,1) = (Byte)((v) >> 8) )
|
||||
#define POKEW(p,v) ( BYTE_(p,0) = (Byte)(v), BYTE_(p,1) = (Byte)((v) >> 8), BYTE_(p,2) = (Byte)((v) >> 16), BYTE_(p,3) = (Byte)((v) >> 24) )
|
||||
|
||||
static Byte s_boot_sector [ BYTES_PER_SECTOR ]; /* boot sector */
|
||||
static Byte s_fsinfo_sector [ BYTES_PER_SECTOR ]; /* FS Info sector */
|
||||
static Byte s_fat_head [ BYTES_PER_SECTOR ]; /* first FAT sector */
|
||||
static Byte s_zero_sector [ BYTES_PER_SECTOR ]; /* empty sector */
|
||||
|
||||
/* this is the date and time when creating the disk */
|
||||
static int
|
||||
get_serial_id( void )
|
||||
{
|
||||
unsigned short lo, hi, mid;
|
||||
time_t now = time(NULL);
|
||||
struct tm tm = gmtime( &now )[0];
|
||||
|
||||
lo = (unsigned short)(tm.tm_mday + ((tm.tm_mon+1) << 8) + (tm.tm_sec << 8));
|
||||
hi = (unsigned short)(tm.tm_min + (tm.tm_hour << 8) + (tm.tm_year + 1900));
|
||||
|
||||
return lo + (hi << 16);
|
||||
}
|
||||
|
||||
static int
|
||||
get_sectors_per_cluster( Wide disk_size )
|
||||
{
|
||||
Wide disk_MB = disk_size/(1024*1024);
|
||||
|
||||
if (disk_MB < 260)
|
||||
return 1;
|
||||
|
||||
if (disk_MB < 8192)
|
||||
return 4;
|
||||
|
||||
if (disk_MB < 16384)
|
||||
return 8;
|
||||
|
||||
if (disk_MB < 32768)
|
||||
return 16;
|
||||
|
||||
return 32;
|
||||
}
|
||||
|
||||
static int
|
||||
get_sectors_per_fat( Wide disk_size, int sectors_per_cluster )
|
||||
{
|
||||
Wide divider;
|
||||
|
||||
/* weird computation from MS - see fatgen103.doc for details */
|
||||
disk_size -= RESERVED_SECTORS * BYTES_PER_SECTOR; /* don't count 32 reserved sectors */
|
||||
disk_size /= BYTES_PER_SECTOR; /* disk size in sectors */
|
||||
divider = ((256 * sectors_per_cluster) + NUM_FATS) / 2;
|
||||
|
||||
return (int)( (disk_size + (divider-1)) / divider );
|
||||
}
|
||||
|
||||
static void
|
||||
boot_sector_init( Bytes boot, Bytes info, Wide disk_size, const char* label )
|
||||
{
|
||||
int sectors_per_cluster = get_sectors_per_cluster(disk_size);
|
||||
int sectors_per_fat = get_sectors_per_fat(disk_size, sectors_per_cluster);
|
||||
int sectors_per_disk = (int)(disk_size / BYTES_PER_SECTOR);
|
||||
int serial_id = get_serial_id();
|
||||
int free_count;
|
||||
|
||||
if (label == NULL)
|
||||
label = "SDCARD";
|
||||
|
||||
POKEB(boot, 0xeb);
|
||||
POKEB(boot+1, 0x5a);
|
||||
POKEB(boot+2, 0x90);
|
||||
strcpy( (char*)boot + 3, "MSWIN4.1" );
|
||||
POKES( boot + 0x0b, BYTES_PER_SECTOR ); /* sector size */
|
||||
POKEB( boot + 0xd, sectors_per_cluster ); /* sectors per cluster */
|
||||
POKES( boot + 0xe, RESERVED_SECTORS ); /* reserved sectors before first FAT */
|
||||
POKEB( boot + 0x10, NUM_FATS ); /* number of FATs */
|
||||
POKES( boot + 0x11, 0 ); /* max root directory entries for FAT12/FAT16, 0 for FAT32 */
|
||||
POKES( boot + 0x13, 0 ); /* total sectors, 0 to use 32-bit value at offset 0x20 */
|
||||
POKEB( boot + 0x15, 0xF8 ); /* media descriptor, 0xF8 == hard disk */
|
||||
POKES( boot + 0x16, 0 ); /* Sectors per FAT for FAT12/16, 0 for FAT32 */
|
||||
POKES( boot + 0x18, 9 ); /* Sectors per track (whatever) */
|
||||
POKES( boot + 0x1a, 2 ); /* Number of heads (whatever) */
|
||||
POKEW( boot + 0x1c, 0 ); /* Hidden sectors */
|
||||
POKEW( boot + 0x20, sectors_per_disk ); /* Total sectors */
|
||||
|
||||
/* extension */
|
||||
POKEW( boot + 0x24, sectors_per_fat ); /* Sectors per FAT */
|
||||
POKES( boot + 0x28, 0 ); /* FAT flags */
|
||||
POKES( boot + 0x2a, 0 ); /* version */
|
||||
POKEW( boot + 0x2c, 2 ); /* cluster number of root directory start */
|
||||
POKES( boot + 0x30, 1 ); /* sector number of FS information sector */
|
||||
POKES( boot + 0x32, BACKUP_BOOT_SECTOR ); /* sector number of a copy of this boot sector */
|
||||
POKEB( boot + 0x40, 0x80 ); /* physical drive number */
|
||||
POKEB( boot + 0x42, 0x29 ); /* extended boot signature ?? */
|
||||
POKEW( boot + 0x43, serial_id ); /* serial ID */
|
||||
strncpy( (char*)boot + 0x47, label, 11 ); /* Volume Label */
|
||||
memcpy( boot + 0x52, "FAT32 ", 8 ); /* FAT system type, padded with 0x20 */
|
||||
|
||||
POKEB( boot + BYTES_PER_SECTOR-2, 0x55 ); /* boot sector signature */
|
||||
POKEB( boot + BYTES_PER_SECTOR-1, 0xAA );
|
||||
|
||||
/* FSInfo sector */
|
||||
free_count = sectors_per_disk - 32 - 2*sectors_per_fat;
|
||||
|
||||
POKEW( info + 0, 0x41615252 );
|
||||
POKEW( info + 484, 0x61417272 );
|
||||
POKEW( info + 488, free_count ); /* number of free clusters */
|
||||
POKEW( info + 492, 3 ); /* next free clusters, 0-1 reserved, 2 is used for the root dir */
|
||||
POKEW( info + 508, 0xAA550000 );
|
||||
}
|
||||
|
||||
static void
|
||||
fat_init( Bytes fat )
|
||||
{
|
||||
POKEW( fat, 0x0ffffff8 ); /* reserve cluster 1, media id in low byte */
|
||||
POKEW( fat + 4, 0x0fffffff ); /* reserve cluster 2 */
|
||||
POKEW( fat + 8, 0x0fffffff ); /* end of clust chain for root dir */
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
write_sector( FILE* file, Bytes sector )
|
||||
{
|
||||
return fwrite( sector, 1, 512, file ) != 512;
|
||||
}
|
||||
|
||||
static int
|
||||
write_empty( FILE* file, Wide count )
|
||||
{
|
||||
static Byte empty[64*1024];
|
||||
|
||||
count *= 512;
|
||||
while (count > 0) {
|
||||
int len = sizeof(empty);
|
||||
if (len > count)
|
||||
len = count;
|
||||
|
||||
if ( fwrite( empty, 1, len, file ) != (size_t)len )
|
||||
return 1;
|
||||
|
||||
count -= len;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void usage (void)
|
||||
{
|
||||
fprintf(stderr, "mksdcard: create a blank FAT32 image to be used with the Android emulator\n" );
|
||||
fprintf(stderr, "usage: mksdcard [-l label] <size> <file>\n\n");
|
||||
fprintf(stderr, " if <size> is a simple integer, it specifies a size in bytes\n" );
|
||||
fprintf(stderr, " if <size> is an integer followed by 'K', it specifies a size in KiB\n" );
|
||||
fprintf(stderr, " if <size> is an integer followed by 'M', it specifies a size in MiB\n" );
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int main( int argc, char** argv )
|
||||
{
|
||||
Wide disk_size;
|
||||
int sectors_per_fat;
|
||||
int sectors_per_disk;
|
||||
char* end;
|
||||
const char* label = NULL;
|
||||
FILE* f;
|
||||
|
||||
for ( ; argc > 1 && argv[1][0] == '-'; argc--, argv++ )
|
||||
{
|
||||
char* arg = argv[1] + 1;
|
||||
switch (arg[0]) {
|
||||
case 'l':
|
||||
if (arg[1] != 0)
|
||||
arg += 2;
|
||||
else {
|
||||
argc--;
|
||||
argv++;
|
||||
if (argc <= 1)
|
||||
usage();
|
||||
arg = argv[1];
|
||||
}
|
||||
label = arg;
|
||||
break;
|
||||
|
||||
default:
|
||||
usage();
|
||||
}
|
||||
}
|
||||
|
||||
if (argc != 3)
|
||||
usage();
|
||||
|
||||
disk_size = strtol( argv[1], &end, 10 );
|
||||
if (disk_size == 0 && errno == EINVAL)
|
||||
usage();
|
||||
|
||||
if (*end == 'K')
|
||||
disk_size *= 1024;
|
||||
else if (*end == 'M')
|
||||
disk_size *= 1024*1024;
|
||||
|
||||
if (disk_size < 8*1024*1024)
|
||||
fprintf(stderr, "### WARNING : SD Card images < 8 MB cannot be used with the Android emulator\n");
|
||||
|
||||
sectors_per_disk = disk_size / 512;
|
||||
sectors_per_fat = get_sectors_per_fat( disk_size, get_sectors_per_cluster( disk_size ) );
|
||||
|
||||
boot_sector_init( s_boot_sector, s_fsinfo_sector, disk_size, NULL );
|
||||
fat_init( s_fat_head );
|
||||
|
||||
f = fopen( argv[2], "wb" );
|
||||
if ( !f ) {
|
||||
fprintf(stderr, "could not create file '%s', aborting...\n", argv[2] );
|
||||
}
|
||||
|
||||
/* here's the layout:
|
||||
*
|
||||
* boot_sector
|
||||
* fsinfo_sector
|
||||
* empty
|
||||
* backup boot sector
|
||||
* backup fsinfo sector
|
||||
* RESERVED_SECTORS - 4 empty sectors (if backup sectors), or RESERVED_SECTORS - 2 (if no backup)
|
||||
* first fat
|
||||
* second fat
|
||||
* zero sectors
|
||||
*/
|
||||
|
||||
if ( write_sector( f, s_boot_sector ) ) goto FailWrite;
|
||||
if ( write_sector( f, s_fsinfo_sector ) ) goto FailWrite;
|
||||
if ( BACKUP_BOOT_SECTOR > 0 ) {
|
||||
if ( write_empty( f, BACKUP_BOOT_SECTOR - 2 ) ) goto FailWrite;
|
||||
if ( write_sector( f, s_boot_sector ) ) goto FailWrite;
|
||||
if ( write_sector( f, s_fsinfo_sector ) ) goto FailWrite;
|
||||
if ( write_empty( f, RESERVED_SECTORS - 2 - BACKUP_BOOT_SECTOR ) ) goto FailWrite;
|
||||
}
|
||||
else
|
||||
if ( write_empty( f, RESERVED_SECTORS - 2 ) ) goto FailWrite;
|
||||
|
||||
if ( write_sector( f, s_fat_head ) ) goto FailWrite;
|
||||
if ( write_empty( f, sectors_per_fat-1 ) ) goto FailWrite;
|
||||
|
||||
if ( write_sector( f, s_fat_head ) ) goto FailWrite;
|
||||
if ( write_empty( f, sectors_per_fat-1 ) ) goto FailWrite;
|
||||
|
||||
if ( write_empty( f, sectors_per_disk - RESERVED_SECTORS - 2*sectors_per_fat ) ) goto FailWrite;
|
||||
|
||||
fclose(f);
|
||||
return 0;
|
||||
|
||||
FailWrite:
|
||||
fprintf(stderr, "could not write to '%s', aborting...\n", argv[2] );
|
||||
unlink( argv[2] );
|
||||
fclose(f);
|
||||
return 1;
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
# Copyright 2008 The Android Open Source Project
|
||||
|
||||
LOCAL_PATH:= $(call my-dir)
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_SRC_FILES:= \
|
||||
qemud.c
|
||||
|
||||
|
||||
LOCAL_SHARED_LIBRARIES := \
|
||||
libcutils \
|
||||
|
||||
LOCAL_MODULE:= qemud
|
||||
LOCAL_MODULE_TAGS := debug
|
||||
|
||||
include $(BUILD_EXECUTABLE)
|
||||
@@ -1,157 +0,0 @@
|
||||
#
|
||||
# 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 := check_stack.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 := check_stack
|
||||
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)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_SRC_FILES := dump_regions.cpp trace_reader.cpp decoder.cpp \
|
||||
read_elf.cpp parse_options.cpp
|
||||
LOCAL_C_INCLUDES += $(common_includes)
|
||||
LOCAL_CFLAGS += $(common_cflags)
|
||||
LOCAL_MODULE := dump_regions
|
||||
include $(BUILD_HOST_EXECUTABLE)
|
||||
@@ -1,905 +0,0 @@
|
||||
// Copyright 2006 The Android Open Source Project
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "armdis.h"
|
||||
#include "opcode.h"
|
||||
|
||||
static const 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 const 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);
|
||||
}
|
||||
|
||||
const 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];
|
||||
|
||||
const char *bang = "";
|
||||
if (write_back)
|
||||
bang = "!";
|
||||
|
||||
const char *carret = "";
|
||||
if (bit_s)
|
||||
carret = "^";
|
||||
|
||||
const 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 = ",";
|
||||
}
|
||||
}
|
||||
|
||||
const 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;
|
||||
|
||||
const char *opname = "ldr";
|
||||
if (!is_load)
|
||||
opname = "str";
|
||||
|
||||
const char *bang = "";
|
||||
if (write_back)
|
||||
bang = "!";
|
||||
|
||||
const char *minus = "";
|
||||
if (is_up == 0)
|
||||
minus = "-";
|
||||
|
||||
const 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 {
|
||||
const 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;
|
||||
}
|
||||
|
||||
const 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);
|
||||
|
||||
const char *opname = "ldr";
|
||||
if (is_load == 0)
|
||||
opname = "str";
|
||||
|
||||
const char *width = "";
|
||||
if (bits_65 == 1)
|
||||
width = "h";
|
||||
else if (bits_65 == 2)
|
||||
width = "sb";
|
||||
else
|
||||
width = "sh";
|
||||
|
||||
const char *bang = "";
|
||||
if (write_back)
|
||||
bang = "!";
|
||||
const 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;
|
||||
|
||||
const 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;
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
// 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 */
|
||||
@@ -1,140 +0,0 @@
|
||||
#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;
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
#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;
|
||||
}
|
||||
@@ -1,222 +0,0 @@
|
||||
#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;
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
// 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
|
||||
@@ -1,775 +0,0 @@
|
||||
// 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 kStartNative = 0x04;
|
||||
static const uint32_t kPopBarrier = (kCausedException | kInterpreted
|
||||
| kStartNative);
|
||||
|
||||
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 FRAME frame_type;
|
||||
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 ever
|
||||
void setNativeOnly(bool nativeOnly) {
|
||||
mNativeOnly = nativeOnly;
|
||||
}
|
||||
|
||||
int getStackLevel() { return mTop; }
|
||||
|
||||
uint64_t getGlobalTime(uint64_t time) { return time + mSkippedTime; }
|
||||
void showStack(FILE *stream);
|
||||
|
||||
int mNumFrames;
|
||||
FRAME *mFrames;
|
||||
int mTop; // index of the next stack frame to write
|
||||
|
||||
private:
|
||||
enum Action { NONE, PUSH, POP, NATIVE_PUSH };
|
||||
|
||||
Action getAction(BBEvent *event, symbol_type *function);
|
||||
void doMethodAction(BBEvent *event, symbol_type *function);
|
||||
void doMethodPop(BBEvent *event, uint32_t addr, const uint32_t flags);
|
||||
void doSimplePush(symbol_type *function, uint32_t addr,
|
||||
uint64_t time, int flags);
|
||||
void doSimplePop(uint64_t time);
|
||||
void doPush(BBEvent *event, symbol_type *function);
|
||||
void doPop(BBEvent *event, symbol_type *function, Action methodAction);
|
||||
|
||||
TraceReaderType *mTrace;
|
||||
|
||||
// This is a global switch that disables Java methods from appearing
|
||||
// on the stack.
|
||||
bool mNativeOnly;
|
||||
|
||||
// This keeps track of whether native frames are currently allowed on the
|
||||
// stack.
|
||||
bool mAllowNativeFrames;
|
||||
|
||||
symbol_type mDummyFunction;
|
||||
region_type mDummyRegion;
|
||||
|
||||
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;
|
||||
mAllowNativeFrames = true;
|
||||
|
||||
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;
|
||||
|
||||
// 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;
|
||||
} else {
|
||||
doMethodAction(event, function);
|
||||
}
|
||||
|
||||
Action action = getAction(event, function);
|
||||
|
||||
// Allow native frames if we are executing in the kernel.
|
||||
if (!mAllowNativeFrames
|
||||
&& (function->region->flags & region_type::kIsKernelRegion) == 0) {
|
||||
action = NONE;
|
||||
}
|
||||
|
||||
if (function->vm_sym != NULL) {
|
||||
function = function->vm_sym;
|
||||
function->vm_sym = NULL;
|
||||
}
|
||||
if (action == PUSH) {
|
||||
doPush(event, function);
|
||||
} else if (action == POP) {
|
||||
doPop(event, function, NONE);
|
||||
}
|
||||
|
||||
#if 0
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// If the stack is now empty, then push the current function.
|
||||
if (mTop == 0) {
|
||||
uint64_t time = event->time - mSkippedTime;
|
||||
int flags = 0;
|
||||
if (function->vm_sym != NULL) {
|
||||
flags = FRAME::kInterpreted;
|
||||
}
|
||||
doSimplePush(function, 0, time, 0);
|
||||
}
|
||||
|
||||
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) {
|
||||
// Don't show the stack by default because this generates a lot
|
||||
// of output and this is seen by users if there is an error when
|
||||
// post-processing the trace. But this is useful for debugging.
|
||||
#if 0
|
||||
showStack(stderr);
|
||||
#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
|
||||
// For debugging only. Show the stack before entering the kernel
|
||||
// exception-handling code.
|
||||
if (function->flags & symbol_type::kIsVectorStart) {
|
||||
printf("stack before entering exception\n");
|
||||
showStack(stderr);
|
||||
}
|
||||
#endif
|
||||
|
||||
// If the top of stack is 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 (mTop > 0
|
||||
&& (mFrames[mTop - 1].function->flags & symbol_type::kIsVectorTable)) {
|
||||
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
|
||||
// For debugging.
|
||||
printf(" doPush(): popping to user mode, bb_addr: 0x%08x\n",
|
||||
event->bb_addr);
|
||||
showStack(stderr);
|
||||
#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
|
||||
// For debugging
|
||||
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;
|
||||
|
||||
// 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.
|
||||
int flags = 0;
|
||||
if (function->vm_sym != NULL) {
|
||||
flags = FRAME::kInterpreted;
|
||||
}
|
||||
doSimplePush(function, retAddr, time, flags);
|
||||
}
|
||||
|
||||
template<class FRAME, class BASE>
|
||||
void CallStack<FRAME, BASE>::doSimplePush(symbol_type *function, uint32_t addr,
|
||||
uint64_t time, int flags)
|
||||
{
|
||||
// Check for stack overflow
|
||||
if (mTop >= mNumFrames) {
|
||||
showStack(stderr);
|
||||
fprintf(stderr, "too many stack frames (%d)\n", mTop);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
mFrames[mTop].addr = addr;
|
||||
mFrames[mTop].function = function;
|
||||
mFrames[mTop].flags = flags;
|
||||
mFrames[mTop].time = time;
|
||||
mFrames[mTop].global_time = time + mSkippedTime;
|
||||
|
||||
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);
|
||||
|
||||
if (mNativeOnly)
|
||||
return;
|
||||
|
||||
// If the stack is empty, then allow more native frames.
|
||||
// Otherwise, if we are transitioning from Java to native, then allow
|
||||
// more native frames.
|
||||
// Otherwise, if we are transitioning from native to Java, then disallow
|
||||
// more native frames.
|
||||
if (mTop == 0) {
|
||||
mAllowNativeFrames = true;
|
||||
} else {
|
||||
bool newerIsJava = (mFrames[mTop].flags & FRAME::kInterpreted) != 0;
|
||||
bool olderIsJava = (mFrames[mTop - 1].flags & FRAME::kInterpreted) != 0;
|
||||
if (newerIsJava && !olderIsJava) {
|
||||
// We are transitioning from Java to native
|
||||
mAllowNativeFrames = true;
|
||||
} else if (!newerIsJava && olderIsJava) {
|
||||
// We are transitioning from native to Java
|
||||
mAllowNativeFrames = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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. But allow popping this frame if this
|
||||
// method called itself and we have a method pop.
|
||||
if (allowMethodPop && function == mPrevFunction) {
|
||||
// pop this frame
|
||||
break;
|
||||
}
|
||||
// do not pop 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 we are popping off a large number of stack frames, then
|
||||
// we might have a bug.
|
||||
if (mTop - stackLevel > 7) {
|
||||
printf("popping thru level %d\n", stackLevel);
|
||||
showStack(stderr);
|
||||
}
|
||||
#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>
|
||||
void CallStack<FRAME, BASE>::doMethodPop(BBEvent *event, uint32_t addr,
|
||||
const uint32_t flags)
|
||||
{
|
||||
uint64_t time = event->time - mSkippedTime;
|
||||
|
||||
// Search the stack from the top down for a frame that contains a
|
||||
// matching method.
|
||||
int stackLevel;
|
||||
for (stackLevel = mTop - 1; stackLevel >= 0; --stackLevel) {
|
||||
if (mFrames[stackLevel].flags & flags) {
|
||||
// If we are searching for a native method, then don't bother trying
|
||||
// to match the address.
|
||||
if (flags == FRAME::kStartNative)
|
||||
break;
|
||||
symbol_type *func = mFrames[stackLevel].function;
|
||||
uint32_t methodAddr = func->region->base_addr + func->addr;
|
||||
if (methodAddr == addr) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we found a matching frame then pop the stack up to and including
|
||||
// that frame.
|
||||
if (stackLevel >= 0) {
|
||||
// Pop the stack frames
|
||||
for (int ii = mTop - 1; ii >= stackLevel; --ii)
|
||||
doSimplePop(time);
|
||||
}
|
||||
}
|
||||
|
||||
template<class FRAME, class BASE>
|
||||
void CallStack<FRAME, BASE>::doMethodAction(BBEvent *event, symbol_type *function)
|
||||
{
|
||||
// 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. Such an unmatched
|
||||
// pop can happen because the user can start tracing at any time and so
|
||||
// there might already be a stack when we start tracing.
|
||||
while (event->time >= sNextMethod.time) {
|
||||
sCurrentMethod = sNextMethod;
|
||||
if (mTrace->ReadMethod(&sNextMethod)) {
|
||||
sNextMethod.time = ~0ull;
|
||||
}
|
||||
}
|
||||
|
||||
if (event->time >= sCurrentMethod.time && event->pid == sCurrentMethod.pid) {
|
||||
uint64_t time = event->time - mSkippedTime;
|
||||
int flags = sCurrentMethod.flags;
|
||||
if (flags == kMethodEnter) {
|
||||
doSimplePush(function, 0, time, FRAME::kInterpreted);
|
||||
mAllowNativeFrames = false;
|
||||
} else if (flags == kNativeEnter) {
|
||||
doSimplePush(function, 0, time, FRAME::kStartNative);
|
||||
mAllowNativeFrames = true;
|
||||
} else if (flags == kMethodExit || flags == kMethodException) {
|
||||
doMethodPop(event, sCurrentMethod.addr, FRAME::kInterpreted);
|
||||
} else if (flags == kNativeExit || flags == kNativeException) {
|
||||
doMethodPop(event, sCurrentMethod.addr, FRAME::kStartNative);
|
||||
}
|
||||
|
||||
// We found a match, so read the next record. When we get to the end
|
||||
// of the trace, we set the time to the maximum value (~0).
|
||||
sCurrentMethod = sNextMethod;
|
||||
if (sNextMethod.time != ~0ull && mTrace->ReadMethod(&sNextMethod)) {
|
||||
sNextMethod.time = ~0ull;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<class FRAME, class BASE>
|
||||
void CallStack<FRAME, BASE>::showStack(FILE *stream)
|
||||
{
|
||||
fprintf(stream, "mTop: %d skippedTime: %llu\n", mTop, mSkippedTime);
|
||||
for (int ii = 0; ii < mTop; ++ii) {
|
||||
uint32_t addr = mFrames[ii].function->addr;
|
||||
addr += mFrames[ii].function->region->vstart;
|
||||
fprintf(stream, " %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, addr,
|
||||
mFrames[ii].function->name);
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* CALL_STACK_H */
|
||||
@@ -1,270 +0,0 @@
|
||||
// Copyright 2009 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"
|
||||
|
||||
typedef CallStack<StackFrame<symbol_type> > CallStackType;
|
||||
|
||||
void compareStacks(uint64_t time, int pid);
|
||||
void dumpStacks(int pid);
|
||||
|
||||
static uint64_t debugTime;
|
||||
static const int kNumStackFrames = 500;
|
||||
static const int kMaxThreads = (32 * 1024);
|
||||
CallStackType *eStacks[kMaxThreads];
|
||||
|
||||
int numErrors;
|
||||
static const int kMaxErrors = 3;
|
||||
|
||||
struct frame {
|
||||
uint64_t time;
|
||||
uint32_t addr;
|
||||
const char *name;
|
||||
bool isNative;
|
||||
|
||||
frame(uint64_t time, uint32_t addr, const char *name, bool isNative) {
|
||||
this->time = time;
|
||||
this->addr = addr;
|
||||
this->name = name;
|
||||
this->isNative = isNative;
|
||||
}
|
||||
};
|
||||
|
||||
class Stack {
|
||||
public:
|
||||
static const int kMaxFrames = 1000;
|
||||
int top;
|
||||
frame *frames[kMaxFrames];
|
||||
|
||||
Stack() {
|
||||
top = 0;
|
||||
}
|
||||
|
||||
void push(frame *pframe);
|
||||
frame* pop();
|
||||
void dump();
|
||||
};
|
||||
|
||||
void Stack::push(frame *pframe) {
|
||||
if (top == kMaxFrames) {
|
||||
fprintf(stderr, "Error: stack overflow\n");
|
||||
exit(1);
|
||||
}
|
||||
frames[top] = pframe;
|
||||
top += 1;
|
||||
}
|
||||
|
||||
frame *Stack::pop() {
|
||||
if (top <= 0)
|
||||
return NULL;
|
||||
top -= 1;
|
||||
return frames[top];
|
||||
}
|
||||
|
||||
Stack *mStacks[kMaxThreads];
|
||||
|
||||
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 *etrace = new TraceReaderType;
|
||||
etrace->Open(qemu_trace_file);
|
||||
etrace->ReadKernelSymbols(elf_file);
|
||||
etrace->SetRoot(root);
|
||||
|
||||
TraceReaderType *mtrace = new TraceReaderType;
|
||||
mtrace->Open(qemu_trace_file);
|
||||
mtrace->ReadKernelSymbols(elf_file);
|
||||
mtrace->SetRoot(root);
|
||||
|
||||
BBEvent event;
|
||||
while (1) {
|
||||
BBEvent ignored;
|
||||
symbol_type *function;
|
||||
MethodRec method_record;
|
||||
symbol_type *sym;
|
||||
TraceReaderType::ProcessState *proc;
|
||||
frame *pframe;
|
||||
|
||||
if (mtrace->ReadMethodSymbol(&method_record, &sym, &proc))
|
||||
break;
|
||||
|
||||
if (!IsValidPid(proc->pid))
|
||||
continue;
|
||||
|
||||
// Get the stack for the current thread
|
||||
Stack *mStack = mStacks[proc->pid];
|
||||
|
||||
// If the stack does not exist, then allocate a new one.
|
||||
if (mStack == NULL) {
|
||||
mStack = new Stack();
|
||||
mStacks[proc->pid] = mStack;
|
||||
}
|
||||
|
||||
int flags = method_record.flags;
|
||||
if (flags == kMethodEnter || flags == kNativeEnter) {
|
||||
pframe = new frame(method_record.time, method_record.addr,
|
||||
sym == NULL ? NULL: sym->name,
|
||||
method_record.flags == kNativeEnter);
|
||||
mStack->push(pframe);
|
||||
} else {
|
||||
pframe = mStack->pop();
|
||||
delete pframe;
|
||||
}
|
||||
|
||||
do {
|
||||
if (GetNextValidEvent(etrace, &event, &ignored, &function))
|
||||
break;
|
||||
if (event.bb_num == 0)
|
||||
break;
|
||||
|
||||
// Get the stack for the current thread
|
||||
CallStackType *eStack = eStacks[event.pid];
|
||||
|
||||
// If the stack does not exist, then allocate a new one.
|
||||
if (eStack == NULL) {
|
||||
eStack = new CallStackType(event.pid, kNumStackFrames, etrace);
|
||||
eStacks[event.pid] = eStack;
|
||||
}
|
||||
if (debugTime != 0 && event.time >= debugTime)
|
||||
printf("time: %llu debug time: %lld\n", event.time, debugTime);
|
||||
|
||||
// Update the stack
|
||||
eStack->updateStack(&event, function);
|
||||
} while (event.time < method_record.time);
|
||||
|
||||
compareStacks(event.time, event.pid);
|
||||
}
|
||||
|
||||
for (int ii = 0; ii < kMaxThreads; ++ii) {
|
||||
if (eStacks[ii])
|
||||
eStacks[ii]->popAll(event.time);
|
||||
}
|
||||
|
||||
delete etrace;
|
||||
delete mtrace;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void compareStacks(uint64_t time, int pid) {
|
||||
CallStackType *eStack = eStacks[pid];
|
||||
Stack *mStack = mStacks[pid];
|
||||
frame **mFrames = mStack->frames;
|
||||
frame *mframe;
|
||||
|
||||
int mTop = mStack->top;
|
||||
int eTop = eStack->mTop;
|
||||
CallStackType::frame_type *eFrames = eStack->mFrames;
|
||||
|
||||
// Count the number of non-native methods (ie, Java methods) on the
|
||||
// Java method stack
|
||||
int numNonNativeMethods = 0;
|
||||
for (int ii = 0; ii < mTop; ++ii) {
|
||||
if (!mFrames[ii]->isNative) {
|
||||
numNonNativeMethods += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Count the number of Java methods on the native stack
|
||||
int numMethods = 0;
|
||||
for (int ii = 0; ii < eTop; ++ii) {
|
||||
if (eFrames[ii].flags & CallStackType::frame_type::kInterpreted) {
|
||||
numMethods += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Verify that the number of Java methods on both stacks are the same.
|
||||
// Allow the native stack to have one less Java method because the
|
||||
// native stack might be pushing a native function first.
|
||||
if (numNonNativeMethods != numMethods && numNonNativeMethods != numMethods + 1) {
|
||||
printf("\nDiff at time %llu pid %d: non-native %d numMethods %d\n",
|
||||
time, pid, numNonNativeMethods, numMethods);
|
||||
dumpStacks(pid);
|
||||
numErrors += 1;
|
||||
if (numErrors >= kMaxErrors)
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Verify that the Java methods on the method stack are the same
|
||||
// as the Java methods on the native stack.
|
||||
int mIndex = 0;
|
||||
for (int ii = 0; ii < eTop; ++ii) {
|
||||
// Ignore native functions on the native stack.
|
||||
if ((eFrames[ii].flags & CallStackType::frame_type::kInterpreted) == 0)
|
||||
continue;
|
||||
uint32_t addr = eFrames[ii].function->addr;
|
||||
addr += eFrames[ii].function->region->vstart;
|
||||
while (mIndex < mTop && mFrames[mIndex]->isNative) {
|
||||
mIndex += 1;
|
||||
}
|
||||
if (mIndex >= mTop)
|
||||
break;
|
||||
if (addr != mFrames[mIndex]->addr) {
|
||||
printf("\nDiff at time %llu pid %d: frame %d\n", time, pid, ii);
|
||||
dumpStacks(pid);
|
||||
exit(1);
|
||||
}
|
||||
mIndex += 1;
|
||||
}
|
||||
}
|
||||
|
||||
void dumpStacks(int pid) {
|
||||
CallStackType *eStack = eStacks[pid];
|
||||
Stack *mStack = mStacks[pid];
|
||||
frame *mframe;
|
||||
|
||||
int mTop = mStack->top;
|
||||
printf("\nJava method stack\n");
|
||||
for (int ii = 0; ii < mTop; ii++) {
|
||||
mframe = mStack->frames[ii];
|
||||
const char *native = mframe->isNative ? "n" : " ";
|
||||
printf(" %s %d: %llu 0x%x %s\n",
|
||||
native, ii, mframe->time, mframe->addr,
|
||||
mframe->name == NULL ? "" : mframe->name);
|
||||
}
|
||||
|
||||
int eTop = eStack->mTop;
|
||||
CallStackType::frame_type *eFrames = eStack->mFrames;
|
||||
int mIndex = 0;
|
||||
printf("\nNative stack\n");
|
||||
for (int ii = 0; ii < eTop; ++ii) {
|
||||
uint32_t addr = eFrames[ii].function->addr;
|
||||
addr += eFrames[ii].function->region->vstart;
|
||||
const char *marker = " ";
|
||||
if (eFrames[ii].flags & CallStackType::frame_type::kInterpreted) {
|
||||
if (mIndex >= mTop || addr != mStack->frames[mIndex]->addr) {
|
||||
marker = "*";
|
||||
}
|
||||
mIndex += 1;
|
||||
}
|
||||
printf(" %s %d: %d f %d 0x%08x %s\n",
|
||||
marker, ii, eFrames[ii].time, eFrames[ii].flags, addr,
|
||||
eFrames[ii].function->name);
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
#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;
|
||||
}
|
||||
@@ -1,153 +0,0 @@
|
||||
#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;
|
||||
const 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;
|
||||
}
|
||||
@@ -1,278 +0,0 @@
|
||||
// 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;
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
// 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_;
|
||||
};
|
||||
@@ -1,255 +0,0 @@
|
||||
// Copyright 2006 The Android Open Source Project
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <inttypes.h>
|
||||
#include <string.h>
|
||||
#include <unistd.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 = (char*)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 = (char*)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);
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
// 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
|
||||
@@ -1,59 +0,0 @@
|
||||
#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"
|
||||
|
||||
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);
|
||||
|
||||
ProcessState *pstate = &processes[0];
|
||||
for (int ii = 0; ii < num_procs; ++ii, ++pstate) {
|
||||
if (pstate->name == NULL)
|
||||
pstate->name = "";
|
||||
ProcessState *manager = pstate->addr_manager;
|
||||
printf("pid %d regions: %d %s",
|
||||
pstate->pid, manager->nregions, pstate->name);
|
||||
for (int jj = 1; jj < pstate->argc; ++jj) {
|
||||
printf(" %s", pstate->argv[jj]);
|
||||
}
|
||||
printf("\n");
|
||||
trace->DumpRegions(stdout, pstate);
|
||||
}
|
||||
|
||||
delete trace;
|
||||
return 0;
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
#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;
|
||||
}
|
||||
@@ -1,152 +0,0 @@
|
||||
#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);
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
// 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
|
||||
@@ -1,219 +0,0 @@
|
||||
// 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);
|
||||
bool Remove(const char *key);
|
||||
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>
|
||||
bool HashTable<T>::Remove(const char *key)
|
||||
{
|
||||
// Hash the key to get the table position
|
||||
int len = strlen(key);
|
||||
int pos = HashFunction(key) & mask_;
|
||||
|
||||
// Search the chain for a matching key and keep track of the previous
|
||||
// element in the chain.
|
||||
entry_type *prev = NULL;
|
||||
for (entry_type *ptr = table_[pos]; ptr; prev = ptr, ptr = ptr->next) {
|
||||
if (strcmp(ptr->key, key) == 0) {
|
||||
if (prev == NULL) {
|
||||
table_[pos] = ptr->next;
|
||||
} else {
|
||||
prev->next = ptr->next;
|
||||
}
|
||||
delete ptr->key;
|
||||
delete ptr;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
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
|
||||
@@ -1,64 +0,0 @@
|
||||
#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;
|
||||
}
|
||||
@@ -1,204 +0,0 @@
|
||||
// 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
|
||||
};
|
||||
@@ -1,166 +0,0 @@
|
||||
// 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
|
||||
@@ -1,155 +0,0 @@
|
||||
// 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 bool IsValidPid(int pid) {
|
||||
if (include_some_pids && pid_include_vector.GetBit(pid) == 0)
|
||||
return false;
|
||||
if (exclude_some_pids && pid_exclude_vector.GetBit(pid))
|
||||
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
|
||||
@@ -1,119 +0,0 @@
|
||||
#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);
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
// 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
|
||||
@@ -1,151 +0,0 @@
|
||||
#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;
|
||||
const 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;
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
#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;
|
||||
const 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 jj = 1; jj < pstate->argc; ++jj) {
|
||||
printf(" %s", pstate->argv[jj]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
delete trace;
|
||||
return 0;
|
||||
}
|
||||
@@ -1,131 +0,0 @@
|
||||
#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;
|
||||
const 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;
|
||||
}
|
||||
@@ -1,274 +0,0 @@
|
||||
// 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;
|
||||
const 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;
|
||||
}
|
||||
@@ -1,108 +0,0 @@
|
||||
// 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;
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
#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;
|
||||
const char *op = "ld";
|
||||
if (flags == 1)
|
||||
op = "st";
|
||||
printf("%lld 0x%08x %s\n", time, addr, op);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -1,210 +0,0 @@
|
||||
/*************************************************************************
|
||||
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;
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
#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 */
|
||||
@@ -1,137 +0,0 @@
|
||||
#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"
|
||||
|
||||
struct frame {
|
||||
uint64_t time;
|
||||
uint32_t addr;
|
||||
const char *name;
|
||||
bool isNative;
|
||||
|
||||
frame(uint64_t time, uint32_t addr, const char *name, bool isNative) {
|
||||
this->time = time;
|
||||
this->addr = addr;
|
||||
this->name = name;
|
||||
this->isNative = isNative;
|
||||
}
|
||||
};
|
||||
|
||||
class Stack {
|
||||
static const int kMaxFrames = 1000;
|
||||
int top;
|
||||
frame *frames[kMaxFrames];
|
||||
|
||||
public:
|
||||
Stack() {
|
||||
top = 0;
|
||||
}
|
||||
|
||||
void push(frame *pframe);
|
||||
frame* pop();
|
||||
void dump();
|
||||
};
|
||||
|
||||
void Stack::push(frame *pframe) {
|
||||
if (top == kMaxFrames) {
|
||||
fprintf(stderr, "Error: stack overflow\n");
|
||||
exit(1);
|
||||
}
|
||||
frames[top] = pframe;
|
||||
top += 1;
|
||||
}
|
||||
|
||||
frame *Stack::pop() {
|
||||
if (top <= 0)
|
||||
return NULL;
|
||||
top -= 1;
|
||||
return frames[top];
|
||||
}
|
||||
|
||||
void Stack::dump() {
|
||||
frame *pframe;
|
||||
|
||||
for (int ii = 0; ii < top; ii++) {
|
||||
pframe = frames[ii];
|
||||
const char *native = pframe->isNative ? "n" : " ";
|
||||
printf(" %s %d: %llu 0x%x %s\n",
|
||||
native, ii, pframe->time, pframe->addr,
|
||||
pframe->name == NULL ? "" : pframe->name);
|
||||
}
|
||||
}
|
||||
|
||||
static const int kMaxThreads = (32 * 1024);
|
||||
Stack *stacks[kMaxThreads];
|
||||
|
||||
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;
|
||||
frame *pframe;
|
||||
|
||||
if (trace->ReadMethodSymbol(&method_record, &sym, &proc))
|
||||
break;
|
||||
|
||||
if (!IsValidPid(proc->pid))
|
||||
continue;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// Get the stack for the current thread
|
||||
Stack *pStack = stacks[proc->pid];
|
||||
|
||||
// If the stack does not exist, then allocate a new one.
|
||||
if (pStack == NULL) {
|
||||
pStack = new Stack();
|
||||
stacks[proc->pid] = pStack;
|
||||
}
|
||||
|
||||
int flags = method_record.flags;
|
||||
if (flags == kMethodEnter || flags == kNativeEnter) {
|
||||
pframe = new frame(method_record.time, method_record.addr,
|
||||
sym == NULL ? NULL: sym->name,
|
||||
method_record.flags == kNativeEnter);
|
||||
pStack->push(pframe);
|
||||
} else {
|
||||
pframe = pStack->pop();
|
||||
delete pframe;
|
||||
}
|
||||
pStack->dump();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
#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;
|
||||
}
|
||||
@@ -1,165 +0,0 @@
|
||||
#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;
|
||||
}
|
||||
@@ -1,156 +0,0 @@
|
||||
// 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"
|
||||
|
||||
static uint64_t debugTime;
|
||||
static uint64_t dumpTime = 0;
|
||||
|
||||
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);
|
||||
void getFrameType(char *type);
|
||||
};
|
||||
|
||||
typedef CallStack<MyFrame> CallStackType;
|
||||
|
||||
void MyFrame::getFrameType(char *type)
|
||||
{
|
||||
strcpy(type, "----");
|
||||
if (flags & kCausedException)
|
||||
type[0] = 'e';
|
||||
if (flags & kInterpreted)
|
||||
type[1] = 'm';
|
||||
if (function->region->flags & region_type::kIsKernelRegion)
|
||||
type[2] = 'k';
|
||||
if (function->flags & symbol_type::kIsVectorTable)
|
||||
type[3] = 'v';
|
||||
}
|
||||
|
||||
void MyFrame::push(int stackLevel, uint64_t time, CallStackBase *base)
|
||||
{
|
||||
char type[5];
|
||||
|
||||
if (dumpTime > 0)
|
||||
return;
|
||||
|
||||
getFrameType(type);
|
||||
printf("%llu en thr %d %s %3d", time, base->getId(), type, 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)
|
||||
{
|
||||
char type[5];
|
||||
|
||||
if (dumpTime > 0)
|
||||
return;
|
||||
|
||||
getFrameType(type);
|
||||
printf("%llu x thr %d %s %3d", time, base->getId(), type, 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];
|
||||
|
||||
void Usage(const char *program)
|
||||
{
|
||||
fprintf(stderr, "Usage: %s [options] [-- -d dumpTime] trace_name elf_file\n",
|
||||
program);
|
||||
OptionsUsage();
|
||||
}
|
||||
|
||||
bool localParseOptions(int argc, char **argv)
|
||||
{
|
||||
bool err = false;
|
||||
while (!err) {
|
||||
int opt = getopt(argc, argv, "+d:");
|
||||
if (opt == -1)
|
||||
break;
|
||||
switch (opt) {
|
||||
case 'd':
|
||||
dumpTime = strtoull(optarg, NULL, 0);
|
||||
break;
|
||||
default:
|
||||
err = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
ParseOptions(argc, argv);
|
||||
localParseOptions(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);
|
||||
|
||||
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);
|
||||
|
||||
// If the user requested a stack dump at a certain time,
|
||||
// and we are at that time, then dump the stack and exit.
|
||||
if (dumpTime > 0 && event.time >= dumpTime) {
|
||||
pStack->showStack(stdout);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (int ii = 0; ii < kMaxThreads; ++ii) {
|
||||
if (stacks[ii])
|
||||
stacks[ii]->popAll(event.time);
|
||||
}
|
||||
|
||||
delete trace;
|
||||
return 0;
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
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 $< $@
|
||||
@@ -1,3 +0,0 @@
|
||||
|
||||
clean:
|
||||
rm -f *.elf *.dis *.bin *.o *~ a.out
|
||||
@@ -1,18 +0,0 @@
|
||||
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
|
||||
@@ -1,201 +0,0 @@
|
||||
#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;
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
#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
|
||||
@@ -1,10 +0,0 @@
|
||||
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) }
|
||||
}
|
||||
@@ -1,503 +0,0 @@
|
||||
/* 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;
|
||||
const 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 const 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;
|
||||
|
||||
const 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 ();
|
||||
}
|
||||
@@ -1,332 +0,0 @@
|
||||
// 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(const 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, const char *filename,
|
||||
const char *tracename, TraceHeader *header);
|
||||
PidEvent *FindMmapDexFileEvent();
|
||||
void ParseDexList(const 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(const 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(const 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(const char *filename, const 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(const 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(const 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(const 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 */
|
||||
@@ -1,29 +0,0 @@
|
||||
# Copyright (C) 2009 The Android Open Source Project
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
ifneq ($(TARGET_PRODUCT),sim)
|
||||
# HAL module implemenation, not prelinked and stored in
|
||||
# hw/<SENSORS_HARDWARE_MODULE_ID>.<ro.hardware>.so
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_PRELINK_MODULE := false
|
||||
LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
|
||||
LOCAL_SHARED_LIBRARIES := liblog libcutils
|
||||
LOCAL_SRC_FILES := sensors_qemu.c
|
||||
LOCAL_MODULE := sensors.goldfish
|
||||
LOCAL_MODULE_TAGS := debug
|
||||
include $(BUILD_SHARED_LIBRARY)
|
||||
endif
|
||||
@@ -1,597 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2009 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/* this implements a sensors hardware library for the Android emulator.
|
||||
* the following code should be built as a shared library that will be
|
||||
* placed into /system/lib/hw/sensors.goldfish.so
|
||||
*
|
||||
* it will be loaded by the code in hardware/libhardware/hardware.c
|
||||
* which is itself called from com_android_server_SensorService.cpp
|
||||
*/
|
||||
|
||||
|
||||
/* we connect with the emulator through the "sensors" qemud service
|
||||
*/
|
||||
#define SENSORS_SERVICE_NAME "sensors"
|
||||
|
||||
#define LOG_TAG "QemuSensors"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <cutils/log.h>
|
||||
#include <cutils/native_handle.h>
|
||||
#include <cutils/sockets.h>
|
||||
#include <hardware/sensors.h>
|
||||
|
||||
#if 0
|
||||
#define D(...) LOGD(__VA_ARGS__)
|
||||
#else
|
||||
#define D(...) ((void)0)
|
||||
#endif
|
||||
|
||||
#define E(...) LOGE(__VA_ARGS__)
|
||||
|
||||
#include <hardware/qemud.h>
|
||||
|
||||
/** SENSOR IDS AND NAMES
|
||||
**/
|
||||
|
||||
#define MAX_NUM_SENSORS 4
|
||||
|
||||
#define SUPPORTED_SENSORS ((1<<MAX_NUM_SENSORS)-1)
|
||||
|
||||
#define ID_BASE SENSORS_HANDLE_BASE
|
||||
#define ID_ACCELERATION (ID_BASE+0)
|
||||
#define ID_MAGNETIC_FIELD (ID_BASE+1)
|
||||
#define ID_ORIENTATION (ID_BASE+2)
|
||||
#define ID_TEMPERATURE (ID_BASE+3)
|
||||
|
||||
#define SENSORS_ACCELERATION (1 << ID_ACCELERATION)
|
||||
#define SENSORS_MAGNETIC_FIELD (1 << ID_MAGNETIC_FIELD)
|
||||
#define SENSORS_ORIENTATION (1 << ID_ORIENTATION)
|
||||
#define SENSORS_TEMPERATURE (1 << ID_TEMPERATURE)
|
||||
|
||||
#define ID_CHECK(x) ((unsigned)((x)-ID_BASE) < 4)
|
||||
|
||||
#define SENSORS_LIST \
|
||||
SENSOR_(ACCELERATION,"acceleration") \
|
||||
SENSOR_(MAGNETIC_FIELD,"magnetic-field") \
|
||||
SENSOR_(ORIENTATION,"orientation") \
|
||||
SENSOR_(TEMPERATURE,"temperature") \
|
||||
|
||||
static const struct {
|
||||
const char* name;
|
||||
int id; } _sensorIds[MAX_NUM_SENSORS] =
|
||||
{
|
||||
#define SENSOR_(x,y) { y, ID_##x },
|
||||
SENSORS_LIST
|
||||
#undef SENSOR_
|
||||
};
|
||||
|
||||
static const char*
|
||||
_sensorIdToName( int id )
|
||||
{
|
||||
int nn;
|
||||
for (nn = 0; nn < MAX_NUM_SENSORS; nn++)
|
||||
if (id == _sensorIds[nn].id)
|
||||
return _sensorIds[nn].name;
|
||||
return "<UNKNOWN>";
|
||||
}
|
||||
|
||||
static int
|
||||
_sensorIdFromName( const char* name )
|
||||
{
|
||||
int nn;
|
||||
|
||||
if (name == NULL)
|
||||
return -1;
|
||||
|
||||
for (nn = 0; nn < MAX_NUM_SENSORS; nn++)
|
||||
if (!strcmp(name, _sensorIds[nn].name))
|
||||
return _sensorIds[nn].id;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/** SENSORS CONTROL DEVICE
|
||||
**
|
||||
** This one is used to send commands to the sensors drivers.
|
||||
** We implement this by sending directly commands to the emulator
|
||||
** through the QEMUD channel.
|
||||
**/
|
||||
|
||||
typedef struct SensorControl {
|
||||
struct sensors_control_device_t device;
|
||||
int fd;
|
||||
uint32_t active_sensors;
|
||||
} SensorControl;
|
||||
|
||||
/* this must return a file descriptor that will be used to read
|
||||
* the sensors data (it is passed to data__data_open() below
|
||||
*/
|
||||
static native_handle_t*
|
||||
control__open_data_source(struct sensors_control_device_t *dev)
|
||||
{
|
||||
SensorControl* ctl = (void*)dev;
|
||||
native_handle_t* handle;
|
||||
|
||||
if (ctl->fd < 0) {
|
||||
ctl->fd = qemud_channel_open(SENSORS_SERVICE_NAME);
|
||||
}
|
||||
D("%s: fd=%d", __FUNCTION__, ctl->fd);
|
||||
handle = native_handle_create(1, 0);
|
||||
handle->data[0] = ctl->fd;
|
||||
return handle;
|
||||
}
|
||||
|
||||
static int
|
||||
control__activate(struct sensors_control_device_t *dev,
|
||||
int handle,
|
||||
int enabled)
|
||||
{
|
||||
SensorControl* ctl = (void*)dev;
|
||||
uint32_t mask, sensors, active, new_sensors, changed;
|
||||
char command[128];
|
||||
int ret;
|
||||
|
||||
D("%s: handle=%s (%d) enabled=%d", __FUNCTION__,
|
||||
_sensorIdToName(handle), handle, enabled);
|
||||
|
||||
if (!ID_CHECK(handle)) {
|
||||
E("%s: bad handle ID", __FUNCTION__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
mask = (1<<handle);
|
||||
sensors = enabled ? mask : 0;
|
||||
|
||||
active = ctl->active_sensors;
|
||||
new_sensors = (active & ~mask) | (sensors & mask);
|
||||
changed = active ^ new_sensors;
|
||||
|
||||
if (!changed)
|
||||
return 0;
|
||||
|
||||
snprintf(command, sizeof command, "set:%s:%d",
|
||||
_sensorIdToName(handle), enabled != 0);
|
||||
|
||||
if (ctl->fd < 0) {
|
||||
ctl->fd = qemud_channel_open(SENSORS_SERVICE_NAME);
|
||||
}
|
||||
|
||||
ret = qemud_channel_send(ctl->fd, command, -1);
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
|
||||
ctl->active_sensors = new_sensors;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
control__set_delay(struct sensors_control_device_t *dev, int32_t ms)
|
||||
{
|
||||
SensorControl* ctl = (void*)dev;
|
||||
char command[128];
|
||||
|
||||
D("%s: dev=%p delay-ms=%d", __FUNCTION__, dev, ms);
|
||||
|
||||
snprintf(command, sizeof command, "set-delay:%d", ms);
|
||||
|
||||
return qemud_channel_send(ctl->fd, command, -1);
|
||||
}
|
||||
|
||||
/* this function is used to force-stop the blocking read() in
|
||||
* data__poll. In order to keep the implementation as simple
|
||||
* as possible here, we send a command to the emulator which
|
||||
* shall send back an appropriate data block to the system.
|
||||
*/
|
||||
static int
|
||||
control__wake(struct sensors_control_device_t *dev)
|
||||
{
|
||||
SensorControl* ctl = (void*)dev;
|
||||
D("%s: dev=%p", __FUNCTION__, dev);
|
||||
return qemud_channel_send(ctl->fd, "wake", -1);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
control__close(struct hw_device_t *dev)
|
||||
{
|
||||
SensorControl* ctl = (void*)dev;
|
||||
close(ctl->fd);
|
||||
free(ctl);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** SENSORS DATA DEVICE
|
||||
**
|
||||
** This one is used to read sensor data from the hardware.
|
||||
** We implement this by simply reading the data from the
|
||||
** emulator through the QEMUD channel.
|
||||
**/
|
||||
|
||||
|
||||
typedef struct SensorData {
|
||||
struct sensors_data_device_t device;
|
||||
sensors_data_t sensors[MAX_NUM_SENSORS];
|
||||
int events_fd;
|
||||
uint32_t pendingSensors;
|
||||
int64_t timeStart;
|
||||
int64_t timeOffset;
|
||||
} SensorData;
|
||||
|
||||
/* return the current time in nanoseconds */
|
||||
static int64_t
|
||||
data__now_ns(void)
|
||||
{
|
||||
struct timespec ts;
|
||||
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
|
||||
return (int64_t)ts.tv_sec * 1000000000 + ts.tv_nsec;
|
||||
}
|
||||
|
||||
static int
|
||||
data__data_open(struct sensors_data_device_t *dev, native_handle_t* handle)
|
||||
{
|
||||
SensorData* data = (void*)dev;
|
||||
int i;
|
||||
D("%s: dev=%p fd=%d", __FUNCTION__, dev, fd);
|
||||
memset(&data->sensors, 0, sizeof(data->sensors));
|
||||
|
||||
for (i=0 ; i<MAX_NUM_SENSORS ; i++) {
|
||||
data->sensors[i].vector.status = SENSOR_STATUS_ACCURACY_HIGH;
|
||||
}
|
||||
data->pendingSensors = 0;
|
||||
data->timeStart = 0;
|
||||
data->timeOffset = 0;
|
||||
|
||||
data->events_fd = dup(handle->data[0]);
|
||||
native_handle_close(handle);
|
||||
native_handle_delete(handle);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
data__data_close(struct sensors_data_device_t *dev)
|
||||
{
|
||||
SensorData* data = (void*)dev;
|
||||
D("%s: dev=%p", __FUNCTION__, dev);
|
||||
if (data->events_fd > 0) {
|
||||
close(data->events_fd);
|
||||
data->events_fd = -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
pick_sensor(SensorData* data,
|
||||
sensors_data_t* values)
|
||||
{
|
||||
uint32_t mask = SUPPORTED_SENSORS;
|
||||
while (mask) {
|
||||
uint32_t i = 31 - __builtin_clz(mask);
|
||||
mask &= ~(1<<i);
|
||||
if (data->pendingSensors & (1<<i)) {
|
||||
data->pendingSensors &= ~(1<<i);
|
||||
*values = data->sensors[i];
|
||||
values->sensor = (1<<i);
|
||||
LOGD_IF(0, "%s: %d [%f, %f, %f]", __FUNCTION__,
|
||||
(1<<i),
|
||||
values->vector.x,
|
||||
values->vector.y,
|
||||
values->vector.z);
|
||||
return i;
|
||||
}
|
||||
}
|
||||
LOGE("No sensor to return!!! pendingSensors=%08x", data->pendingSensors);
|
||||
// we may end-up in a busy loop, slow things down, just in case.
|
||||
usleep(100000);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int
|
||||
data__poll(struct sensors_data_device_t *dev, sensors_data_t* values)
|
||||
{
|
||||
SensorData* data = (void*)dev;
|
||||
int fd = data->events_fd;
|
||||
|
||||
D("%s: data=%p", __FUNCTION__, dev);
|
||||
|
||||
// there are pending sensors, returns them now...
|
||||
if (data->pendingSensors) {
|
||||
return pick_sensor(data, values);
|
||||
}
|
||||
|
||||
// wait until we get a complete event for an enabled sensor
|
||||
uint32_t new_sensors = 0;
|
||||
|
||||
while (1) {
|
||||
/* read the next event */
|
||||
char buff[256];
|
||||
int len = qemud_channel_recv(data->events_fd, buff, sizeof buff-1);
|
||||
float params[3];
|
||||
int64_t event_time;
|
||||
|
||||
if (len < 0)
|
||||
continue;
|
||||
|
||||
buff[len] = 0;
|
||||
|
||||
/* "wake" is sent from the emulator to exit this loop. This shall
|
||||
* really be because another thread called "control__wake" in this
|
||||
* process.
|
||||
*/
|
||||
if (!strcmp((const char*)data, "wake")) {
|
||||
return 0x7FFFFFFF;
|
||||
}
|
||||
|
||||
/* "acceleration:<x>:<y>:<z>" corresponds to an acceleration event */
|
||||
if (sscanf(buff, "acceleration:%g:%g:%g", params+0, params+1, params+2) == 3) {
|
||||
new_sensors |= SENSORS_ACCELERATION;
|
||||
data->sensors[ID_ACCELERATION].acceleration.x = params[0];
|
||||
data->sensors[ID_ACCELERATION].acceleration.y = params[1];
|
||||
data->sensors[ID_ACCELERATION].acceleration.z = params[2];
|
||||
continue;
|
||||
}
|
||||
|
||||
/* "orientation:<azimuth>:<pitch>:<roll>" is sent when orientation changes */
|
||||
if (sscanf(buff, "orientation:%g:%g:%g", params+0, params+1, params+2) == 3) {
|
||||
new_sensors |= SENSORS_ORIENTATION;
|
||||
data->sensors[ID_ORIENTATION].orientation.azimuth = params[0];
|
||||
data->sensors[ID_ORIENTATION].orientation.pitch = params[1];
|
||||
data->sensors[ID_ORIENTATION].orientation.roll = params[2];
|
||||
continue;
|
||||
}
|
||||
|
||||
/* "magnetic:<x>:<y>:<z>" is sent for the params of the magnetic field */
|
||||
if (sscanf(buff, "magnetic:%g:%g:%g", params+0, params+1, params+2) == 3) {
|
||||
new_sensors |= SENSORS_MAGNETIC_FIELD;
|
||||
data->sensors[ID_MAGNETIC_FIELD].magnetic.x = params[0];
|
||||
data->sensors[ID_MAGNETIC_FIELD].magnetic.y = params[1];
|
||||
data->sensors[ID_MAGNETIC_FIELD].magnetic.z = params[2];
|
||||
continue;
|
||||
}
|
||||
|
||||
/* "temperature:<celsius>" */
|
||||
if (sscanf(buff, "temperature:%g", params+0) == 2) {
|
||||
new_sensors |= SENSORS_TEMPERATURE;
|
||||
data->sensors[ID_TEMPERATURE].temperature = params[0];
|
||||
continue;
|
||||
}
|
||||
|
||||
/* "sync:<time>" is sent after a series of sensor events.
|
||||
* where 'time' is expressed in micro-seconds and corresponds
|
||||
* to the VM time when the real poll occured.
|
||||
*/
|
||||
if (sscanf(buff, "sync:%lld", &event_time) == 1) {
|
||||
if (new_sensors) {
|
||||
data->pendingSensors = new_sensors;
|
||||
int64_t t = event_time * 1000LL; /* convert to nano-seconds */
|
||||
|
||||
/* use the time at the first sync: as the base for later
|
||||
* time values */
|
||||
if (data->timeStart == 0) {
|
||||
data->timeStart = data__now_ns();
|
||||
data->timeOffset = data->timeStart - t;
|
||||
}
|
||||
t += data->timeOffset;
|
||||
|
||||
while (new_sensors) {
|
||||
uint32_t i = 31 - __builtin_clz(new_sensors);
|
||||
new_sensors &= ~(1<<i);
|
||||
data->sensors[i].time = t;
|
||||
}
|
||||
return pick_sensor(data, values);
|
||||
} else {
|
||||
D("huh ? sync without any sensor data ?");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
D("huh ? unsupported command");
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
data__close(struct hw_device_t *dev)
|
||||
{
|
||||
SensorData* data = (SensorData*)dev;
|
||||
if (data) {
|
||||
if (data->events_fd > 0) {
|
||||
//LOGD("(device close) about to close fd=%d", data->events_fd);
|
||||
close(data->events_fd);
|
||||
}
|
||||
free(data);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/** MODULE REGISTRATION SUPPORT
|
||||
**
|
||||
** This is required so that hardware/libhardware/hardware.c
|
||||
** will dlopen() this library appropriately.
|
||||
**/
|
||||
|
||||
/*
|
||||
* the following is the list of all supported sensors.
|
||||
* this table is used to build sSensorList declared below
|
||||
* according to which hardware sensors are reported as
|
||||
* available from the emulator (see get_sensors_list below)
|
||||
*
|
||||
* note: numerical values for maxRange/resolution/power were
|
||||
* taken from the reference AK8976A implementation
|
||||
*/
|
||||
static const struct sensor_t sSensorListInit[] = {
|
||||
{ .name = "Goldfish 3-axis Accelerometer",
|
||||
.vendor = "The Android Open Source Project",
|
||||
.version = 1,
|
||||
.handle = ID_ACCELERATION,
|
||||
.type = SENSOR_TYPE_ACCELEROMETER,
|
||||
.maxRange = 2.8f,
|
||||
.resolution = 1.0f/4032.0f,
|
||||
.power = 3.0f,
|
||||
.reserved = {}
|
||||
},
|
||||
|
||||
{ .name = "Goldfish 3-axis Magnetic field sensor",
|
||||
.vendor = "The Android Open Source Project",
|
||||
.version = 1,
|
||||
.handle = ID_MAGNETIC_FIELD,
|
||||
.type = SENSOR_TYPE_MAGNETIC_FIELD,
|
||||
.maxRange = 2000.0f,
|
||||
.resolution = 1.0f,
|
||||
.power = 6.7f,
|
||||
.reserved = {}
|
||||
},
|
||||
|
||||
{ .name = "Goldfish Orientation sensor",
|
||||
.vendor = "The Android Open Source Project",
|
||||
.version = 1,
|
||||
.handle = ID_ORIENTATION,
|
||||
.type = SENSOR_TYPE_ORIENTATION,
|
||||
.maxRange = 360.0f,
|
||||
.resolution = 1.0f,
|
||||
.power = 9.7f,
|
||||
.reserved = {}
|
||||
},
|
||||
|
||||
{ .name = "Goldfish Temperature sensor",
|
||||
.vendor = "The Android Open Source Project",
|
||||
.version = 1,
|
||||
.handle = ID_TEMPERATURE,
|
||||
.type = SENSOR_TYPE_TEMPERATURE,
|
||||
.maxRange = 80.0f,
|
||||
.resolution = 1.0f,
|
||||
.power = 0.0f,
|
||||
.reserved = {}
|
||||
},
|
||||
};
|
||||
|
||||
static struct sensor_t sSensorList[MAX_NUM_SENSORS];
|
||||
|
||||
static uint32_t sensors__get_sensors_list(struct sensors_module_t* module,
|
||||
struct sensor_t const** list)
|
||||
{
|
||||
int fd = qemud_channel_open(SENSORS_SERVICE_NAME);
|
||||
char buffer[12];
|
||||
int mask, nn, count;
|
||||
|
||||
int ret;
|
||||
if (fd < 0) {
|
||||
E("%s: no qemud connection", __FUNCTION__);
|
||||
return 0;
|
||||
}
|
||||
ret = qemud_channel_send(fd, "list-sensors", -1);
|
||||
if (ret < 0) {
|
||||
E("%s: could not query sensor list: %s", __FUNCTION__,
|
||||
strerror(errno));
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
ret = qemud_channel_recv(fd, buffer, sizeof buffer-1);
|
||||
if (ret < 0) {
|
||||
E("%s: could not receive sensor list: %s", __FUNCTION__,
|
||||
strerror(errno));
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
buffer[ret] = 0;
|
||||
close(fd);
|
||||
|
||||
/* the result is a integer used as a mask for available sensors */
|
||||
mask = atoi(buffer);
|
||||
count = 0;
|
||||
for (nn = 0; nn < MAX_NUM_SENSORS; nn++) {
|
||||
if (((1 << nn) & mask) == 0)
|
||||
continue;
|
||||
|
||||
sSensorList[count++] = sSensorListInit[nn];
|
||||
}
|
||||
D("%s: returned %d sensors (mask=%d)", __FUNCTION__, count, mask);
|
||||
*list = sSensorList;
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
open_sensors(const struct hw_module_t* module,
|
||||
const char* name,
|
||||
struct hw_device_t* *device)
|
||||
{
|
||||
int status = -EINVAL;
|
||||
|
||||
D("%s: name=%s", __FUNCTION__, name);
|
||||
|
||||
if (!strcmp(name, SENSORS_HARDWARE_CONTROL))
|
||||
{
|
||||
SensorControl *dev = malloc(sizeof(*dev));
|
||||
|
||||
memset(dev, 0, sizeof(*dev));
|
||||
|
||||
dev->device.common.tag = HARDWARE_DEVICE_TAG;
|
||||
dev->device.common.version = 0;
|
||||
dev->device.common.module = (struct hw_module_t*) module;
|
||||
dev->device.common.close = control__close;
|
||||
dev->device.open_data_source = control__open_data_source;
|
||||
dev->device.activate = control__activate;
|
||||
dev->device.set_delay = control__set_delay;
|
||||
dev->device.wake = control__wake;
|
||||
dev->fd = -1;
|
||||
|
||||
*device = &dev->device.common;
|
||||
status = 0;
|
||||
}
|
||||
else if (!strcmp(name, SENSORS_HARDWARE_DATA)) {
|
||||
SensorData *dev = malloc(sizeof(*dev));
|
||||
|
||||
memset(dev, 0, sizeof(*dev));
|
||||
|
||||
dev->device.common.tag = HARDWARE_DEVICE_TAG;
|
||||
dev->device.common.version = 0;
|
||||
dev->device.common.module = (struct hw_module_t*) module;
|
||||
dev->device.common.close = data__close;
|
||||
dev->device.data_open = data__data_open;
|
||||
dev->device.data_close = data__data_close;
|
||||
dev->device.poll = data__poll;
|
||||
dev->events_fd = -1;
|
||||
|
||||
*device = &dev->device.common;
|
||||
status = 0;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
static struct hw_module_methods_t sensors_module_methods = {
|
||||
.open = open_sensors
|
||||
};
|
||||
|
||||
const struct sensors_module_t HAL_MODULE_INFO_SYM = {
|
||||
.common = {
|
||||
.tag = HARDWARE_MODULE_TAG,
|
||||
.version_major = 1,
|
||||
.version_minor = 0,
|
||||
.id = SENSORS_HARDWARE_MODULE_ID,
|
||||
.name = "Goldfish SENSORS Module",
|
||||
.author = "The Android Open Source Project",
|
||||
.methods = &sensors_module_methods,
|
||||
},
|
||||
.get_sensors_list = sensors__get_sensors_list
|
||||
};
|
||||
|
Before Width: | Height: | Size: 449 B |
|
Before Width: | Height: | Size: 825 B |
|
Before Width: | Height: | Size: 795 B |
|
Before Width: | Height: | Size: 453 B |
|
Before Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 592 B |
|
Before Width: | Height: | Size: 19 KiB |
@@ -1,2 +0,0 @@
|
||||
# skin-specific hardware values
|
||||
hw.lcd.density=160
|
||||
|
Before Width: | Height: | Size: 154 B |
|
Before Width: | Height: | Size: 20 KiB |
@@ -1,443 +0,0 @@
|
||||
parts {
|
||||
portrait {
|
||||
background {
|
||||
image background_port.png
|
||||
}
|
||||
}
|
||||
landscape {
|
||||
background {
|
||||
image background_land.png
|
||||
}
|
||||
}
|
||||
|
||||
device {
|
||||
display {
|
||||
width 320
|
||||
height 480
|
||||
x 0
|
||||
y 0
|
||||
}
|
||||
}
|
||||
|
||||
controls {
|
||||
background {
|
||||
image controls.png
|
||||
}
|
||||
buttons {
|
||||
soft-left {
|
||||
image button.png
|
||||
x 56
|
||||
y 142
|
||||
}
|
||||
home {
|
||||
image button.png
|
||||
x 0
|
||||
y 142
|
||||
}
|
||||
back {
|
||||
image button.png
|
||||
x 112
|
||||
y 142
|
||||
}
|
||||
dpad-up {
|
||||
image arrow_up.png
|
||||
x 77
|
||||
y 53
|
||||
}
|
||||
dpad-down {
|
||||
image arrow_down.png
|
||||
x 77
|
||||
y 106
|
||||
}
|
||||
dpad-left {
|
||||
image arrow_left.png
|
||||
x 53
|
||||
y 53
|
||||
}
|
||||
dpad-right {
|
||||
image arrow_right.png
|
||||
x 123
|
||||
y 53
|
||||
}
|
||||
dpad-center {
|
||||
image select.png
|
||||
x 77
|
||||
y 81
|
||||
}
|
||||
phone-dial {
|
||||
image button.png
|
||||
x 0
|
||||
y 71
|
||||
}
|
||||
phone-hangup {
|
||||
image button.png
|
||||
x 168
|
||||
y 71
|
||||
}
|
||||
|
||||
power {
|
||||
image button.png
|
||||
x 168
|
||||
y 0
|
||||
}
|
||||
|
||||
volume-up {
|
||||
image button.png
|
||||
x 112
|
||||
y 0
|
||||
}
|
||||
|
||||
volume-down {
|
||||
image button.png
|
||||
x 56
|
||||
y 0
|
||||
}
|
||||
|
||||
search {
|
||||
image button.png
|
||||
x 168
|
||||
y 142
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
keyboard {
|
||||
background {
|
||||
image keyboard.png
|
||||
}
|
||||
buttons {
|
||||
1 {
|
||||
image key.png
|
||||
x 5
|
||||
y 5
|
||||
}
|
||||
2 {
|
||||
image key.png
|
||||
x 42
|
||||
y 5
|
||||
}
|
||||
3 {
|
||||
image key.png
|
||||
x 79
|
||||
y 5
|
||||
}
|
||||
4 {
|
||||
image key.png
|
||||
x 116
|
||||
y 5
|
||||
}
|
||||
5 {
|
||||
image key.png
|
||||
x 153
|
||||
y 5
|
||||
}
|
||||
6 {
|
||||
image key.png
|
||||
x 190
|
||||
y 5
|
||||
}
|
||||
7 {
|
||||
image key.png
|
||||
x 227
|
||||
y 5
|
||||
}
|
||||
8 {
|
||||
image key.png
|
||||
x 264
|
||||
y 5
|
||||
}
|
||||
9 {
|
||||
image key.png
|
||||
x 301
|
||||
y 5
|
||||
}
|
||||
0 {
|
||||
image key.png
|
||||
x 338
|
||||
y 5
|
||||
}
|
||||
|
||||
q {
|
||||
image key.png
|
||||
x 5
|
||||
y 41
|
||||
}
|
||||
w {
|
||||
image key.png
|
||||
x 42
|
||||
y 41
|
||||
}
|
||||
e {
|
||||
image key.png
|
||||
x 79
|
||||
y 41
|
||||
}
|
||||
r {
|
||||
image key.png
|
||||
x 116
|
||||
y 41
|
||||
}
|
||||
t {
|
||||
image key.png
|
||||
x 153
|
||||
y 41
|
||||
}
|
||||
y {
|
||||
image key.png
|
||||
x 190
|
||||
y 41
|
||||
}
|
||||
u {
|
||||
image key.png
|
||||
x 227
|
||||
y 41
|
||||
}
|
||||
i {
|
||||
image key.png
|
||||
x 264
|
||||
y 41
|
||||
}
|
||||
o {
|
||||
image key.png
|
||||
x 301
|
||||
y 41
|
||||
}
|
||||
p {
|
||||
image key.png
|
||||
x 338
|
||||
y 41
|
||||
}
|
||||
|
||||
a {
|
||||
image key.png
|
||||
x 5
|
||||
y 77
|
||||
}
|
||||
s {
|
||||
image key.png
|
||||
x 42
|
||||
y 77
|
||||
}
|
||||
d {
|
||||
image key.png
|
||||
x 79
|
||||
y 77
|
||||
}
|
||||
f {
|
||||
image key.png
|
||||
x 116
|
||||
y 77
|
||||
}
|
||||
g {
|
||||
image key.png
|
||||
x 153
|
||||
y 77
|
||||
}
|
||||
h {
|
||||
image key.png
|
||||
x 190
|
||||
y 77
|
||||
}
|
||||
j {
|
||||
image key.png
|
||||
x 227
|
||||
y 77
|
||||
}
|
||||
k {
|
||||
image key.png
|
||||
x 264
|
||||
y 77
|
||||
}
|
||||
l {
|
||||
image key.png
|
||||
x 301
|
||||
y 77
|
||||
}
|
||||
DEL {
|
||||
image key.png
|
||||
x 338
|
||||
y 77
|
||||
}
|
||||
|
||||
CAP {
|
||||
image key.png
|
||||
x 5
|
||||
y 113
|
||||
}
|
||||
z {
|
||||
image key.png
|
||||
x 42
|
||||
y 113
|
||||
}
|
||||
x {
|
||||
image key.png
|
||||
x 79
|
||||
y 113
|
||||
}
|
||||
c {
|
||||
image key.png
|
||||
x 116
|
||||
y 113
|
||||
}
|
||||
v {
|
||||
image key.png
|
||||
x 153
|
||||
y 113
|
||||
}
|
||||
b {
|
||||
image key.png
|
||||
x 190
|
||||
y 113
|
||||
}
|
||||
n {
|
||||
image key.png
|
||||
x 227
|
||||
y 113
|
||||
}
|
||||
m {
|
||||
image key.png
|
||||
x 264
|
||||
y 113
|
||||
}
|
||||
PERIOD {
|
||||
image key.png
|
||||
x 301
|
||||
y 113
|
||||
}
|
||||
ENTER {
|
||||
image key.png
|
||||
x 338
|
||||
y 113
|
||||
}
|
||||
|
||||
ALT {
|
||||
image key.png
|
||||
x 5
|
||||
y 149
|
||||
}
|
||||
SYM {
|
||||
image key.png
|
||||
x 42
|
||||
y 149
|
||||
}
|
||||
AT {
|
||||
image key.png
|
||||
x 79
|
||||
y 149
|
||||
}
|
||||
SPACE {
|
||||
image spacebar.png
|
||||
x 116
|
||||
y 149
|
||||
}
|
||||
SLASH {
|
||||
image key.png
|
||||
x 264
|
||||
y 149
|
||||
}
|
||||
COMMA {
|
||||
image key.png
|
||||
x 301
|
||||
y 149
|
||||
}
|
||||
ALT2 {
|
||||
image key.png
|
||||
x 338
|
||||
y 149
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
layouts {
|
||||
portrait {
|
||||
width 791
|
||||
height 534
|
||||
color 0xe0e0e0
|
||||
event EV_SW:0:1
|
||||
|
||||
part1 {
|
||||
name portrait
|
||||
x 0
|
||||
y 0
|
||||
}
|
||||
|
||||
part2 {
|
||||
name landscape
|
||||
x 800
|
||||
y 0
|
||||
}
|
||||
|
||||
part3 {
|
||||
name device
|
||||
x 28
|
||||
y 27
|
||||
}
|
||||
part4 {
|
||||
name controls
|
||||
x 476
|
||||
y 77
|
||||
}
|
||||
part5 {
|
||||
name keyboard
|
||||
x 395
|
||||
y 328
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
landscape {
|
||||
width 640
|
||||
height 601
|
||||
color 0xe0e0e0
|
||||
event EV_SW:0:0
|
||||
|
||||
# the framework _always_ assume that the DPad
|
||||
# has been physically rotated in landscape mode.
|
||||
# however, with this skin, this is not the case
|
||||
#
|
||||
dpad-rotation 3
|
||||
|
||||
part1 {
|
||||
name portrait
|
||||
x 800
|
||||
y 0
|
||||
}
|
||||
|
||||
part2 {
|
||||
name landscape
|
||||
x 0
|
||||
y 0
|
||||
}
|
||||
|
||||
part3 {
|
||||
name device
|
||||
x 80
|
||||
y 349
|
||||
rotation 3
|
||||
}
|
||||
|
||||
part4 {
|
||||
name controls
|
||||
x 410
|
||||
y 396
|
||||
}
|
||||
|
||||
part5 {
|
||||
name keyboard
|
||||
x 18
|
||||
y 396
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
keyboard {
|
||||
charmap qwerty2
|
||||
}
|
||||
|
||||
network {
|
||||
speed full
|
||||
delay none
|
||||
}
|
||||
|
Before Width: | Height: | Size: 384 B |
|
Before Width: | Height: | Size: 192 B |
|
Before Width: | Height: | Size: 449 B |
|
Before Width: | Height: | Size: 825 B |
|
Before Width: | Height: | Size: 795 B |
|
Before Width: | Height: | Size: 453 B |
|
Before Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 592 B |
|
Before Width: | Height: | Size: 19 KiB |
@@ -1,2 +0,0 @@
|
||||
# skin-specific hardware values
|
||||
hw.lcd.density=120
|
||||
|
Before Width: | Height: | Size: 154 B |
|
Before Width: | Height: | Size: 20 KiB |
@@ -1,439 +0,0 @@
|
||||
parts {
|
||||
portrait {
|
||||
background {
|
||||
image background_port.png
|
||||
}
|
||||
}
|
||||
landscape {
|
||||
background {
|
||||
image background_land.png
|
||||
}
|
||||
}
|
||||
|
||||
device {
|
||||
display {
|
||||
width 240
|
||||
height 320
|
||||
x 0
|
||||
y 0
|
||||
}
|
||||
}
|
||||
|
||||
controls {
|
||||
background {
|
||||
image controls.png
|
||||
}
|
||||
buttons {
|
||||
soft-left {
|
||||
image button.png
|
||||
x 56
|
||||
y 142
|
||||
}
|
||||
home {
|
||||
image button.png
|
||||
x 0
|
||||
y 142
|
||||
}
|
||||
back {
|
||||
image button.png
|
||||
x 112
|
||||
y 142
|
||||
}
|
||||
dpad-up {
|
||||
image arrow_up.png
|
||||
x 77
|
||||
y 53
|
||||
}
|
||||
dpad-down {
|
||||
image arrow_down.png
|
||||
x 77
|
||||
y 106
|
||||
}
|
||||
dpad-left {
|
||||
image arrow_left.png
|
||||
x 53
|
||||
y 53
|
||||
}
|
||||
dpad-right {
|
||||
image arrow_right.png
|
||||
x 123
|
||||
y 53
|
||||
}
|
||||
dpad-center {
|
||||
image select.png
|
||||
x 77
|
||||
y 81
|
||||
}
|
||||
phone-dial {
|
||||
image button.png
|
||||
x 0
|
||||
y 71
|
||||
}
|
||||
phone-hangup {
|
||||
image button.png
|
||||
x 168
|
||||
y 71
|
||||
}
|
||||
|
||||
power {
|
||||
image button.png
|
||||
x 168
|
||||
y 0
|
||||
}
|
||||
|
||||
volume-up {
|
||||
image button.png
|
||||
x 112
|
||||
y 0
|
||||
}
|
||||
|
||||
volume-down {
|
||||
image button.png
|
||||
x 56
|
||||
y 0
|
||||
}
|
||||
|
||||
search {
|
||||
image button.png
|
||||
x 168
|
||||
y 142
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
keyboard {
|
||||
background {
|
||||
image keyboard.png
|
||||
}
|
||||
buttons {
|
||||
1 {
|
||||
image key.png
|
||||
x 5
|
||||
y 5
|
||||
}
|
||||
2 {
|
||||
image key.png
|
||||
x 42
|
||||
y 5
|
||||
}
|
||||
3 {
|
||||
image key.png
|
||||
x 79
|
||||
y 5
|
||||
}
|
||||
4 {
|
||||
image key.png
|
||||
x 116
|
||||
y 5
|
||||
}
|
||||
5 {
|
||||
image key.png
|
||||
x 153
|
||||
y 5
|
||||
}
|
||||
6 {
|
||||
image key.png
|
||||
x 190
|
||||
y 5
|
||||
}
|
||||
7 {
|
||||
image key.png
|
||||
x 227
|
||||
y 5
|
||||
}
|
||||
8 {
|
||||
image key.png
|
||||
x 264
|
||||
y 5
|
||||
}
|
||||
9 {
|
||||
image key.png
|
||||
x 301
|
||||
y 5
|
||||
}
|
||||
0 {
|
||||
image key.png
|
||||
x 338
|
||||
y 5
|
||||
}
|
||||
|
||||
q {
|
||||
image key.png
|
||||
x 5
|
||||
y 41
|
||||
}
|
||||
w {
|
||||
image key.png
|
||||
x 42
|
||||
y 41
|
||||
}
|
||||
e {
|
||||
image key.png
|
||||
x 79
|
||||
y 41
|
||||
}
|
||||
r {
|
||||
image key.png
|
||||
x 116
|
||||
y 41
|
||||
}
|
||||
t {
|
||||
image key.png
|
||||
x 153
|
||||
y 41
|
||||
}
|
||||
y {
|
||||
image key.png
|
||||
x 190
|
||||
y 41
|
||||
}
|
||||
u {
|
||||
image key.png
|
||||
x 227
|
||||
y 41
|
||||
}
|
||||
i {
|
||||
image key.png
|
||||
x 264
|
||||
y 41
|
||||
}
|
||||
o {
|
||||
image key.png
|
||||
x 301
|
||||
y 41
|
||||
}
|
||||
p {
|
||||
image key.png
|
||||
x 338
|
||||
y 41
|
||||
}
|
||||
|
||||
a {
|
||||
image key.png
|
||||
x 5
|
||||
y 77
|
||||
}
|
||||
s {
|
||||
image key.png
|
||||
x 42
|
||||
y 77
|
||||
}
|
||||
d {
|
||||
image key.png
|
||||
x 79
|
||||
y 77
|
||||
}
|
||||
f {
|
||||
image key.png
|
||||
x 116
|
||||
y 77
|
||||
}
|
||||
g {
|
||||
image key.png
|
||||
x 153
|
||||
y 77
|
||||
}
|
||||
h {
|
||||
image key.png
|
||||
x 190
|
||||
y 77
|
||||
}
|
||||
j {
|
||||
image key.png
|
||||
x 227
|
||||
y 77
|
||||
}
|
||||
k {
|
||||
image key.png
|
||||
x 264
|
||||
y 77
|
||||
}
|
||||
l {
|
||||
image key.png
|
||||
x 301
|
||||
y 77
|
||||
}
|
||||
DEL {
|
||||
image key.png
|
||||
x 338
|
||||
y 77
|
||||
}
|
||||
|
||||
CAP {
|
||||
image key.png
|
||||
x 5
|
||||
y 113
|
||||
}
|
||||
z {
|
||||
image key.png
|
||||
x 42
|
||||
y 113
|
||||
}
|
||||
x {
|
||||
image key.png
|
||||
x 79
|
||||
y 113
|
||||
}
|
||||
c {
|
||||
image key.png
|
||||
x 116
|
||||
y 113
|
||||
}
|
||||
v {
|
||||
image key.png
|
||||
x 153
|
||||
y 113
|
||||
}
|
||||
b {
|
||||
image key.png
|
||||
x 190
|
||||
y 113
|
||||
}
|
||||
n {
|
||||
image key.png
|
||||
x 227
|
||||
y 113
|
||||
}
|
||||
m {
|
||||
image key.png
|
||||
x 264
|
||||
y 113
|
||||
}
|
||||
PERIOD {
|
||||
image key.png
|
||||
x 301
|
||||
y 113
|
||||
}
|
||||
ENTER {
|
||||
image key.png
|
||||
x 338
|
||||
y 113
|
||||
}
|
||||
|
||||
ALT {
|
||||
image key.png
|
||||
x 5
|
||||
y 149
|
||||
}
|
||||
SYM {
|
||||
image key.png
|
||||
x 42
|
||||
y 149
|
||||
}
|
||||
AT {
|
||||
image key.png
|
||||
x 79
|
||||
y 149
|
||||
}
|
||||
SPACE {
|
||||
image spacebar.png
|
||||
x 116
|
||||
y 149
|
||||
}
|
||||
SLASH {
|
||||
image key.png
|
||||
x 264
|
||||
y 149
|
||||
}
|
||||
COMMA {
|
||||
image key.png
|
||||
x 301
|
||||
y 149
|
||||
}
|
||||
ALT2 {
|
||||
image key.png
|
||||
x 338
|
||||
y 149
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
layouts {
|
||||
portrait {
|
||||
width 711
|
||||
height 435
|
||||
color 0xe0e0e0
|
||||
event EV_SW:0:1
|
||||
|
||||
part1 {
|
||||
name portrait
|
||||
x 0
|
||||
y 0
|
||||
}
|
||||
|
||||
part2 {
|
||||
name landscape
|
||||
x 800
|
||||
y 0
|
||||
}
|
||||
|
||||
part3 {
|
||||
name device
|
||||
x 28
|
||||
y 58
|
||||
}
|
||||
part4 {
|
||||
name controls
|
||||
x 396
|
||||
y 27
|
||||
}
|
||||
part5 {
|
||||
name keyboard
|
||||
x 315
|
||||
y 229
|
||||
}
|
||||
}
|
||||
|
||||
landscape {
|
||||
width 640
|
||||
height 522
|
||||
color 0xe0e0e0
|
||||
event EV_SW:0:0
|
||||
|
||||
dpad-rotation 3
|
||||
|
||||
|
||||
part1 {
|
||||
name portrait
|
||||
x 800
|
||||
y 0
|
||||
}
|
||||
|
||||
part2 {
|
||||
name landscape
|
||||
x 0
|
||||
y 0
|
||||
}
|
||||
|
||||
part3 {
|
||||
name device
|
||||
x 160
|
||||
y 270
|
||||
rotation 3
|
||||
}
|
||||
|
||||
part4 {
|
||||
name controls
|
||||
x 410
|
||||
y 317
|
||||
}
|
||||
|
||||
part5 {
|
||||
name keyboard
|
||||
x 18
|
||||
y 317
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
keyboard {
|
||||
charmap qwerty2
|
||||
}
|
||||
|
||||
network {
|
||||
speed full
|
||||
delay none
|
||||
}
|
||||
|
Before Width: | Height: | Size: 384 B |
|
Before Width: | Height: | Size: 192 B |
|
Before Width: | Height: | Size: 449 B |
|
Before Width: | Height: | Size: 825 B |
|
Before Width: | Height: | Size: 795 B |
|
Before Width: | Height: | Size: 453 B |
|
Before Width: | Height: | Size: 106 KiB |
|
Before Width: | Height: | Size: 114 KiB |
|
Before Width: | Height: | Size: 592 B |
|
Before Width: | Height: | Size: 19 KiB |
@@ -1,2 +0,0 @@
|
||||
# skin-specific hardware values
|
||||
hw.lcd.density=120
|
||||