Merge changes from topic 'e2fsprogs'
* changes:
ext4_utils: move ext2simg to upstream e2fsprogs
ext4_utils: use mke2fs instead of make_ext4
diff --git a/bootctl/Android.mk b/bootctl/Android.mk
index f0f5a06..04dbf8d 100644
--- a/bootctl/Android.mk
+++ b/bootctl/Android.mk
@@ -3,10 +3,13 @@
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
-LOCAL_SRC_FILES := bootctl.c
-LOCAL_SHARED_LIBRARIES := libhardware
+LOCAL_SRC_FILES := bootctl.cpp
LOCAL_MODULE := bootctl
-LOCAL_C_INCLUDES = hardware/libhardware/include
-LOCAL_CFLAGS := -Wno-unused-parameter
+LOCAL_SHARED_LIBRARIES := \
+ libhidlbase \
+ libhidltransport \
+ libhwbinder \
+ libutils \
+ android.hardware.boot@1.0 \
include $(BUILD_EXECUTABLE)
diff --git a/bootctl/bootctl.c b/bootctl/bootctl.c
deleted file mode 100644
index ee8378b..0000000
--- a/bootctl/bootctl.c
+++ /dev/null
@@ -1,212 +0,0 @@
-/*
- * Copyright (C) 2015 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.
- */
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <sysexits.h>
-
-#include <errno.h>
-#include <limits.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <hardware/hardware.h>
-#include <hardware/boot_control.h>
-
-static void usage(FILE* where, int argc, char* argv[])
-{
- fprintf(where,
- "%s - command-line wrapper for the boot_control HAL.\n"
- "\n"
- "Usage:\n"
- " %s COMMAND\n"
- "\n"
- "Commands:\n"
- " %s hal-info - Show info about boot_control HAL used.\n"
- " %s get-number-slots - Prints number of slots.\n"
- " %s get-current-slot - Prints currently running SLOT.\n"
- " %s mark-boot-successful - Mark current slot as GOOD.\n"
- " %s set-active-boot-slot SLOT - On next boot, load and execute SLOT.\n"
- " %s set-slot-as-unbootable SLOT - Mark SLOT as invalid.\n"
- " %s is-slot-bootable SLOT - Returns 0 only if SLOT is bootable.\n"
- " %s is-slot-marked-successful SLOT - Returns 0 only if SLOT is marked GOOD.\n"
- " %s get-suffix SLOT - Prints suffix for SLOT.\n"
- "\n"
- "SLOT parameter is the zero-based slot-number.\n",
- argv[0], argv[0], argv[0], argv[0], argv[0], argv[0],
- argv[0], argv[0], argv[0], argv[0], argv[0]);
-}
-
-static int do_hal_info(const hw_module_t *hw_module)
-{
- fprintf(stdout,
- "HAL name: %s\n"
- "HAL author: %s\n"
- "HAL module version: %d.%d\n",
- hw_module->name,
- hw_module->author,
- hw_module->module_api_version>>8,
- hw_module->module_api_version&0xff);
- return EX_OK;
-}
-
-static int do_get_number_slots(boot_control_module_t *module)
-{
- int num_slots = module->getNumberSlots(module);
- fprintf(stdout, "%d\n", num_slots);
- return EX_OK;
-}
-
-static int do_get_current_slot(boot_control_module_t *module)
-{
- int cur_slot = module->getCurrentSlot(module);
- fprintf(stdout, "%d\n", cur_slot);
- return EX_OK;
-}
-
-static int do_mark_boot_successful(boot_control_module_t *module)
-{
- int ret = module->markBootSuccessful(module);
- if (ret == 0)
- return EX_OK;
- fprintf(stderr, "Error marking as having booted successfully: %s\n",
- strerror(-ret));
- return EX_SOFTWARE;
-}
-
-static int do_set_active_boot_slot(boot_control_module_t *module,
- int slot_number)
-{
- int ret = module->setActiveBootSlot(module, slot_number);
- if (ret == 0)
- return EX_OK;
- fprintf(stderr, "Error setting active boot slot: %s\n", strerror(-ret));
- return EX_SOFTWARE;
-}
-
-static int do_set_slot_as_unbootable(boot_control_module_t *module,
- int slot_number)
-{
- int ret = module->setSlotAsUnbootable(module, slot_number);
- if (ret == 0)
- return EX_OK;
- fprintf(stderr, "Error setting slot as unbootable: %s\n", strerror(-ret));
- return EX_SOFTWARE;
-}
-
-
-static int do_is_slot_bootable(boot_control_module_t *module, int slot_number)
-{
- int ret = module->isSlotBootable(module, slot_number);
- if (ret == 0) {
- return EX_SOFTWARE;
- } else if (ret < 0) {
- fprintf(stderr, "Error calling isSlotBootable(): %s\n",
- strerror(-ret));
- return EX_SOFTWARE;
- }
- return EX_OK;
-}
-
-
-static int do_get_suffix(boot_control_module_t *module, int slot_number)
-{
- const char* suffix = module->getSuffix(module, slot_number);
- fprintf(stdout, "%s\n", suffix);
- return EX_OK;
-}
-
-static int do_is_slot_marked_successful(boot_control_module_t *module,
- int slot_number)
-{
- if (module->isSlotMarkedSuccessful == NULL) {
- fprintf(stderr, "isSlotMarkedSuccessful() is not implemented by HAL.\n");
- return EX_UNAVAILABLE;
- }
- int ret = module->isSlotMarkedSuccessful(module, slot_number);
- if (ret == 0) {
- return EX_SOFTWARE;
- } else if (ret < 0) {
- fprintf(stderr, "Error calling isSlotMarkedSuccessful(): %s\n",
- strerror(-ret));
- return EX_SOFTWARE;
- }
- return EX_OK;
-}
-
-static int parse_slot(int pos, int argc, char *argv[])
-{
- if (pos > argc - 1) {
- usage(stderr, argc, argv);
- exit(EX_USAGE);
- return -1;
- }
- errno = 0;
- long int ret = strtol(argv[pos], NULL, 10);
- if (errno != 0 || ret > INT_MAX || ret < 0) {
- usage(stderr, argc, argv);
- exit(EX_USAGE);
- return -1;
- }
- return (int)ret;
-}
-
-int main(int argc, char *argv[])
-{
- const hw_module_t *hw_module;
- boot_control_module_t *module;
- int ret;
-
- if (argc < 2) {
- usage(stderr, argc, argv);
- return EX_USAGE;
- }
-
- ret = hw_get_module("bootctrl", &hw_module);
- if (ret != 0) {
- fprintf(stderr, "Error getting bootctrl module.\n");
- return EX_SOFTWARE;
- }
- module = (boot_control_module_t*) hw_module;
- module->init(module);
-
- if (strcmp(argv[1], "hal-info") == 0) {
- return do_hal_info(hw_module);
- } else if (strcmp(argv[1], "get-number-slots") == 0) {
- return do_get_number_slots(module);
- } else if (strcmp(argv[1], "get-current-slot") == 0) {
- return do_get_current_slot(module);
- } else if (strcmp(argv[1], "mark-boot-successful") == 0) {
- return do_mark_boot_successful(module);
- } else if (strcmp(argv[1], "set-active-boot-slot") == 0) {
- return do_set_active_boot_slot(module, parse_slot(2, argc, argv));
- } else if (strcmp(argv[1], "set-slot-as-unbootable") == 0) {
- return do_set_slot_as_unbootable(module, parse_slot(2, argc, argv));
- } else if (strcmp(argv[1], "is-slot-bootable") == 0) {
- return do_is_slot_bootable(module, parse_slot(2, argc, argv));
- } else if (strcmp(argv[1], "get-suffix") == 0) {
- return do_get_suffix(module, parse_slot(2, argc, argv));
- } else if (strcmp(argv[1], "is-slot-marked-successful") == 0) {
- return do_is_slot_marked_successful(module, parse_slot(2, argc, argv));
- } else {
- usage(stderr, argc, argv);
- return EX_USAGE;
- }
-
- return 0;
-}
diff --git a/bootctl/bootctl.cpp b/bootctl/bootctl.cpp
new file mode 100644
index 0000000..12baf83
--- /dev/null
+++ b/bootctl/bootctl.cpp
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#include <sysexits.h>
+#include <android/hardware/boot/1.0/IBootControl.h>
+
+using android::sp;
+
+using android::hardware::hidl_string;
+using android::hardware::Return;
+
+using android::hardware::boot::V1_0::BoolResult;
+using android::hardware::boot::V1_0::IBootControl;
+using android::hardware::boot::V1_0::CommandResult;
+using android::hardware::boot::V1_0::Slot;
+
+static void usage(FILE* where, int /* argc */, char* argv[])
+{
+ fprintf(where,
+ "%s - command-line wrapper for the boot HAL.\n"
+ "\n"
+ "Usage:\n"
+ " %s COMMAND\n"
+ "\n"
+ "Commands:\n"
+ " %s hal-info - Show info about boot_control HAL used.\n"
+ " %s get-number-slots - Prints number of slots.\n"
+ " %s get-current-slot - Prints currently running SLOT.\n"
+ " %s mark-boot-successful - Mark current slot as GOOD.\n"
+ " %s set-active-boot-slot SLOT - On next boot, load and execute SLOT.\n"
+ " %s set-slot-as-unbootable SLOT - Mark SLOT as invalid.\n"
+ " %s is-slot-bootable SLOT - Returns 0 only if SLOT is bootable.\n"
+ " %s is-slot-marked-successful SLOT - Returns 0 only if SLOT is marked GOOD.\n"
+ " %s get-suffix SLOT - Prints suffix for SLOT.\n"
+ "\n"
+ "SLOT parameter is the zero-based slot-number.\n",
+ argv[0], argv[0], argv[0], argv[0], argv[0], argv[0],
+ argv[0], argv[0], argv[0], argv[0], argv[0]);
+}
+
+static int do_hal_info(const sp<IBootControl> module) {
+ fprintf(stdout,
+ "HAL module version: %u.%u\n",
+ module->getInterfaceVersion().get_major(),
+ module->getInterfaceVersion().get_minor());
+ return EX_OK;
+}
+
+static int do_get_number_slots(sp<IBootControl> module)
+{
+ uint32_t numSlots = module->getNumberSlots();
+ fprintf(stdout, "%u\n", numSlots);
+ return EX_OK;
+}
+
+static int do_get_current_slot(sp<IBootControl> module)
+{
+ Slot curSlot = module->getCurrentSlot();
+ fprintf(stdout, "%u\n", curSlot);
+ return EX_OK;
+}
+
+static std::function<void(CommandResult)> generate_callback(CommandResult *crp) {
+ return [=](CommandResult cr){
+ *crp = cr;
+ };
+}
+
+static int handle_return(Return<void> ret, CommandResult cr, const char* errStr) {
+ if (!ret.getStatus().isOk()) {
+ fprintf(stderr, errStr, ret.getStatus().exceptionMessage().string());
+ return EX_SOFTWARE;
+ } else if (!cr.success) {
+ fprintf(stderr, errStr, cr.errMsg.c_str());
+ return EX_SOFTWARE;
+ }
+ return EX_OK;
+}
+
+static int do_mark_boot_successful(sp<IBootControl> module)
+{
+ CommandResult cr;
+ Return<void> ret = module->markBootSuccessful(generate_callback(&cr));
+ return handle_return(ret, cr, "Error marking as having booted successfully: %s\n");
+}
+
+static int do_set_active_boot_slot(sp<IBootControl> module,
+ Slot slot_number)
+{
+ CommandResult cr;
+ Return<void> ret = module->setActiveBootSlot(slot_number, generate_callback(&cr));
+ return handle_return(ret, cr, "Error setting active boot slot: %s\n");
+}
+
+static int do_set_slot_as_unbootable(sp<IBootControl> module,
+ Slot slot_number)
+{
+ CommandResult cr;
+ Return<void> ret = module->setSlotAsUnbootable(slot_number, generate_callback(&cr));
+ return handle_return(ret, cr, "Error setting slot as unbootable: %s\n");
+}
+
+static int handle_return(Return<BoolResult> ret, const char* errStr) {
+ if (!ret.getStatus().isOk()) {
+ fprintf(stderr, errStr, ret.getStatus().exceptionMessage().string());
+ return EX_SOFTWARE;
+ } else if (ret == BoolResult::INVALID_SLOT) {
+ fprintf(stderr, errStr, "Invalid slot");
+ return EX_SOFTWARE;
+ } else if (ret == BoolResult::TRUE) {
+ return EX_OK;
+ }
+ return EX_SOFTWARE;
+}
+
+static int do_is_slot_bootable(sp<IBootControl> module, Slot slot_number)
+{
+ Return<BoolResult> ret = module->isSlotBootable(slot_number);
+ return handle_return(ret, "Error calling isSlotBootable(): %s\n");
+}
+
+static int do_is_slot_marked_successful(sp<IBootControl> module,
+ Slot slot_number)
+{
+ Return<BoolResult> ret = module->isSlotMarkedSuccessful(slot_number);
+ return handle_return(ret, "Error calling isSlotMarkedSuccessful(): %s\n");
+}
+
+
+static int do_get_suffix(sp<IBootControl> module, Slot slot_number) {
+ std::function<void(hidl_string)> cb = [](hidl_string suffix){
+ fprintf(stdout, "%s\n", suffix.c_str());
+ };
+ Return<void> ret = module->getSuffix(slot_number, cb);
+ if (!ret.getStatus().isOk()) {
+ fprintf(stderr, "Error calling getSuffix(): %s\n",
+ ret.getStatus().exceptionMessage().string());
+ return EX_SOFTWARE;
+ }
+ return EX_OK;
+}
+
+static uint32_t parse_slot(int pos, int argc, char *argv[])
+{
+ if (pos > argc - 1) {
+ usage(stderr, argc, argv);
+ exit(EX_USAGE);
+ return -1;
+ }
+ errno = 0;
+ uint64_t ret = strtoul(argv[pos], NULL, 10);
+ if (errno != 0 || ret > UINT_MAX) {
+ usage(stderr, argc, argv);
+ exit(EX_USAGE);
+ return -1;
+ }
+ return (uint32_t)ret;
+}
+
+int main(int argc, char *argv[])
+{
+ sp<IBootControl> module;
+ int ret;
+
+ if (argc < 2) {
+ usage(stderr, argc, argv);
+ return EX_USAGE;
+ }
+
+ module = IBootControl::getService("bootctrl");
+ if (module == NULL) {
+ fprintf(stderr, "Error getting bootctrl module.\n");
+ return EX_SOFTWARE;
+ }
+
+ if (strcmp(argv[1], "hal-info") == 0) {
+ return do_hal_info(module);
+ } else if (strcmp(argv[1], "get-number-slots") == 0) {
+ return do_get_number_slots(module);
+ } else if (strcmp(argv[1], "get-current-slot") == 0) {
+ return do_get_current_slot(module);
+ } else if (strcmp(argv[1], "mark-boot-successful") == 0) {
+ return do_mark_boot_successful(module);
+ } else if (strcmp(argv[1], "set-active-boot-slot") == 0) {
+ return do_set_active_boot_slot(module, parse_slot(2, argc, argv));
+ } else if (strcmp(argv[1], "set-slot-as-unbootable") == 0) {
+ return do_set_slot_as_unbootable(module, parse_slot(2, argc, argv));
+ } else if (strcmp(argv[1], "is-slot-bootable") == 0) {
+ return do_is_slot_bootable(module, parse_slot(2, argc, argv));
+ } else if (strcmp(argv[1], "get-suffix") == 0) {
+ return do_get_suffix(module, parse_slot(2, argc, argv));
+ } else if (strcmp(argv[1], "is-slot-marked-successful") == 0) {
+ return do_is_slot_marked_successful(module, parse_slot(2, argc, argv));
+ } else {
+ usage(stderr, argc, argv);
+ return EX_USAGE;
+ }
+
+ return 0;
+}
diff --git a/ext4_utils/allocate.c b/ext4_utils/allocate.c
index 6f5e4d3..00f2203 100644
--- a/ext4_utils/allocate.c
+++ b/ext4_utils/allocate.c
@@ -352,7 +352,8 @@
static struct region *ext4_allocate_best_fit_partial(u32 len)
{
- unsigned int i, j;
+ unsigned int i;
+ int j;
unsigned int found_bg = 0, found_prev_chunk = 0, found_block = 0;
u32 found_allocate_len = 0;
bool minimize = false;
diff --git a/ext4_utils/ext4fixup.c b/ext4_utils/ext4fixup.c
index f9285d0..508e8bd 100644
--- a/ext4_utils/ext4fixup.c
+++ b/ext4_utils/ext4fixup.c
@@ -16,7 +16,9 @@
#include "ext4fixup.h"
-#define _LARGEFILE64_SOURCE
+#ifndef _LARGEFILE64_SOURCE
+#define _LARGEFILE64_SOURCE 1
+#endif
#include <fcntl.h>
#include <sys/stat.h>
diff --git a/ext4_utils/make_ext4fs.c b/ext4_utils/make_ext4fs.c
index a1cfe06..b59000d 100644
--- a/ext4_utils/make_ext4fs.c
+++ b/ext4_utils/make_ext4fs.c
@@ -538,7 +538,8 @@
}
static int get_block_group(u32 block) {
- int i, group = 0;
+ unsigned int i, group = 0;
+
for(i = 0; i < aux_info.groups; i++) {
if (block >= aux_info.bgs[i].first_block)
group = i;
@@ -559,7 +560,8 @@
char stored_file_name[MAX_PATH], real_file_name[MAX_PATH], file_map[MAX_BLK_MAPPING_STR];
struct block_allocation *fs_alloc;
struct block_group_info *bgs = aux_info.bgs;
- int i, major_version = 0, minor_version = 0;
+ int major_version = 0, minor_version = 0;
+ unsigned int i;
char *base_file_line = NULL;
size_t base_file_line_len = 0;
@@ -596,7 +598,8 @@
if (!access(real_file_name, R_OK)) {
char *block_range, *end_string;
int real_file_fd;
- u32 start_block, end_block, block_file_size;
+ int start_block, end_block;
+ u32 block_file_size;
u32 real_file_block_size;
real_file_fd = open(real_file_name, O_RDONLY);
diff --git a/f2fs_utils/f2fs_sparseblock.c b/f2fs_utils/f2fs_sparseblock.c
index e968ce0..d5c1f3a 100644
--- a/f2fs_utils/f2fs_sparseblock.c
+++ b/f2fs_utils/f2fs_sparseblock.c
@@ -2,17 +2,19 @@
#define LOG_TAG "f2fs_sparseblock"
-
-#include <cutils/log.h>
#include <errno.h>
-#include <fcntl.h>
#include <f2fs_fs.h>
+#include <fcntl.h>
#include <linux/types.h>
#include <malloc.h>
#include <string.h>
#include <sys/stat.h>
-#include "f2fs_sparseblock.h"
+#include <sys/types.h>
+#include <unistd.h>
+#include <log/log.h>
+
+#include "f2fs_sparseblock.h"
#define D_DISP_u32(ptr, member) \
do { \
diff --git a/memtrack/memtrack.cpp b/memtrack/memtrack.cpp
index 2c4d7c0..f14e06d 100644
--- a/memtrack/memtrack.cpp
+++ b/memtrack/memtrack.cpp
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+#define LOG_TAG "MemTracker"
#include "memtrack.h"
@@ -27,15 +28,10 @@
#include <sys/types.h>
#include <unistd.h>
-#include <cutils/log.h>
-
#include <algorithm>
#include <vector>
-#ifdef LOG_TAG
-#undef LOG_TAG
-#endif
-#define LOG_TAG "MemTracker"
+#include <android/log.h>
FileData::FileData(char *filename, char *buffer, size_t buffer_len)
: data_(buffer), max_(buffer_len), cur_idx_(0), len_(0),
diff --git a/simpleperf/Android.mk b/simpleperf/Android.mk
index 3c40292..e993906 100644
--- a/simpleperf/Android.mk
+++ b/simpleperf/Android.mk
@@ -13,7 +13,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-
LOCAL_PATH := $(call my-dir)
simpleperf_version := $(shell git -C $(LOCAL_PATH) rev-parse --short=12 HEAD 2>/dev/null)
@@ -43,6 +42,7 @@
libbase \
libcutils \
liblog \
+ libprocinfo \
libutils \
liblzma \
libLLVMObject \
@@ -71,6 +71,7 @@
libprotobuf-cpp-lite \
simpleperf_static_libraries_host_linux := \
+ libprocinfo \
libbacktrace_offline \
libbacktrace \
libunwind \
@@ -154,7 +155,7 @@
LOCAL_STATIC_LIBRARIES := $(simpleperf_static_libraries_host)
LOCAL_STATIC_LIBRARIES_linux := $(simpleperf_static_libraries_host_linux)
LOCAL_LDLIBS_linux := $(simpleperf_ldlibs_host_linux)
-LOCAL_MULTILIB := first
+LOCAL_MULTILIB := both
LOCAL_PROTOC_OPTIMIZE_TYPE := lite-static
LOCAL_CXX_STL := libc++_static
include $(LLVM_HOST_BUILD_MK)
@@ -231,7 +232,7 @@
LOCAL_STATIC_LIBRARIES := libsimpleperf $(simpleperf_static_libraries_host)
LOCAL_STATIC_LIBRARIES_linux := $(simpleperf_static_libraries_host_linux)
LOCAL_LDLIBS_linux := $(simpleperf_ldlibs_host_linux) -Wl,--exclude-libs,ALL
-LOCAL_MULTILIB := first
+LOCAL_MULTILIB := both
LOCAL_CXX_STL := libc++_static
include $(LLVM_HOST_BUILD_MK)
include $(BUILD_HOST_SHARED_LIBRARY)
@@ -278,7 +279,7 @@
$($(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_OBJCOPY) --add-section .testzipdata=$$TMP_FILE $(linked_module) && \
rm -f $$TMP_FILE
-LOCAL_MULTILIB := first
+LOCAL_MULTILIB := both
LOCAL_FORCE_STATIC_EXECUTABLE := true
include $(LLVM_DEVICE_BUILD_MK)
include $(BUILD_NATIVE_TEST)
diff --git a/simpleperf/IOEventLoop_test.cpp b/simpleperf/IOEventLoop_test.cpp
index 90bb4fa..dc7a4da 100644
--- a/simpleperf/IOEventLoop_test.cpp
+++ b/simpleperf/IOEventLoop_test.cpp
@@ -18,6 +18,7 @@
#include <gtest/gtest.h>
+#include <atomic>
#include <chrono>
#include <thread>
@@ -105,13 +106,15 @@
}
return true;
}));
- std::thread thread([]() {
- for (int i = 0; i < 100; ++i) {
+ std::atomic<bool> stop_thread(false);
+ std::thread thread([&]() {
+ while (!stop_thread) {
usleep(1000);
kill(getpid(), SIGINT);
}
});
ASSERT_TRUE(loop.RunLoop());
+ stop_thread = true;
thread.join();
ASSERT_EQ(100, count);
}
diff --git a/simpleperf/README.md b/simpleperf/README.md
index 3acd33a..96f601b 100644
--- a/simpleperf/README.md
+++ b/simpleperf/README.md
@@ -178,8 +178,6 @@
# Print stat for process 11904 every 300ms.
$simpleperf stat -p 11904 --duration 10 --interval 300
- # Stop by using Ctrl-C.
- ^C
# Print system wide stat at interval of 300ms for 10 seconds (rooted device only).
# system wide profiling needs root privilege
@@ -378,9 +376,10 @@
sudo-game-jni.so. We focus on sudo-game-jni.so, not the java code or system
libraries.
-## 1. Run debug version of the app on device
-We need to run debug version of the app, because we can’t use *run-as* for non
-debuggable apps.
+## 1. Run a debuggable="true" version of the app on device
+We need to run a copy of the app with android:debuggable=”true” in its
+AndroidManfest.xml <application> element, because we can’t use run-as for
+non-debuggable apps.
## 2. Download simpleperf to the app’s directory
Use *uname* to find the architecture on device
@@ -632,6 +631,40 @@
$adb shell run-as com.example.sudogame ./simpleperf report -n -g --symfs . >perf.report
$python report.py perf.report
+## 8. Report perf.data on host
+We can also use adb to pull perf.data on host. Then use simpleperf on host to
+report it (Simpleperf on host is not provided, but can be built from source
+code). Because we don’t have any symbol information on host, we need to
+collect symbol information in perf.data while recording.
+
+ # Collect symbol information while recording.
+ device#./simpleperf record -t 25636 --dump-symbols --duration 30
+
+ # pull perf.data on host
+ host$adb shell run-as com.example.sudogame cat perf.data >perf.data
+
+ # report perf.data
+ host$simpleperf report
+
+### Show flamegraph
+Simpleperf supports reading perf.data through libsimpleperf_report.so.
+Currently, libsimpleperf_report.so is only provided on linux x86_64 platform,
+but it can be built for other platforms from source code. It has a python
+interface simpleperf_report_lib.py. So we can write python scripts to read
+perf.data. The shared library and scripts are in
+https://android.googlesource.com/platform/system/extras/+/master/simpleperf/scripts/.
+
+One example is report_sample.py. It can be used to output file used to show
+flame graph as below.
+
+ # Convert perf.data into out.perf.
+ host$python report_sample.py >out.perf
+
+ # show out.perf using flamegraph
+ host$stackcollapse-perf.pl out.perf >out.folded
+ host$./flamegraph.pl out.folded >a.svg
+
+
# Steps to profile java code on rooted devices
Simpleperf only supports profiling native instructions in binaries in ELF
format. If the java code is executed by interpreter, or with jit cache, it
diff --git a/simpleperf/cmd_record.cpp b/simpleperf/cmd_record.cpp
index 8d9e5ac..2d1e012 100644
--- a/simpleperf/cmd_record.cpp
+++ b/simpleperf/cmd_record.cpp
@@ -242,6 +242,7 @@
if (!SetEventSelectionFlags()) {
return false;
}
+ ScopedCurrentArch scoped_arch(GetMachineArch());
// 2. Create workload.
std::unique_ptr<Workload> workload;
@@ -828,14 +829,15 @@
(r.GetValidStackSize() > 0)) {
ThreadEntry* thread =
thread_tree_.FindThreadOrNew(r.tid_data.pid, r.tid_data.tid);
- RegSet regs =
- CreateRegSet(r.regs_user_data.reg_mask, r.regs_user_data.regs);
- ArchType arch = GetArchForAbi(GetBuildArch(), r.regs_user_data.abi);
+ RegSet regs = CreateRegSet(r.regs_user_data.abi,
+ r.regs_user_data.reg_mask,
+ r.regs_user_data.regs);
// Normally do strict arch check when unwinding stack. But allow unwinding
// 32-bit processes on 64-bit devices for system wide profiling.
bool strict_arch_check = !system_wide_collection_;
std::vector<uint64_t> unwind_ips =
- UnwindCallChain(arch, *thread, regs, r.stack_user_data.data,
+ UnwindCallChain(r.regs_user_data.abi, *thread, regs,
+ r.stack_user_data.data,
r.GetValidStackSize(), strict_arch_check);
r.ReplaceRegAndStackWithCallChain(unwind_ips);
}
diff --git a/simpleperf/cmd_record_test.cpp b/simpleperf/cmd_record_test.cpp
index 07cf930..35f330e 100644
--- a/simpleperf/cmd_record_test.cpp
+++ b/simpleperf/cmd_record_test.cpp
@@ -88,7 +88,8 @@
if (record->type() == PERF_RECORD_MMAP) {
const MmapRecord* mmap_record =
static_cast<const MmapRecord*>(record.get());
- if (strcmp(mmap_record->filename, DEFAULT_KERNEL_MMAP_NAME) == 0) {
+ if (strcmp(mmap_record->filename, DEFAULT_KERNEL_MMAP_NAME) == 0 ||
+ strcmp(mmap_record->filename, DEFAULT_KERNEL_MMAP_NAME_PERF) == 0) {
have_kernel_mmap = true;
break;
}
@@ -140,10 +141,13 @@
TEST(record_cmd, dwarf_callchain_sampling) {
if (IsDwarfCallChainSamplingSupported()) {
- ASSERT_TRUE(RunRecordCmd({"--call-graph", "dwarf"}));
- ASSERT_TRUE(RunRecordCmd({"--call-graph", "dwarf,16384"}));
- ASSERT_FALSE(RunRecordCmd({"--call-graph", "dwarf,65536"}));
- ASSERT_TRUE(RunRecordCmd({"-g"}));
+ std::vector<std::unique_ptr<Workload>> workloads;
+ CreateProcesses(1, &workloads);
+ std::string pid = std::to_string(workloads[0]->GetPid());
+ ASSERT_TRUE(RunRecordCmd({"-p", pid, "--call-graph", "dwarf"}));
+ ASSERT_TRUE(RunRecordCmd({"-p", pid, "--call-graph", "dwarf,16384"}));
+ ASSERT_FALSE(RunRecordCmd({"-p", pid, "--call-graph", "dwarf,65536"}));
+ ASSERT_TRUE(RunRecordCmd({"-p", pid, "-g"}));
} else {
GTEST_LOG_(INFO) << "This test does nothing as dwarf callchain sampling is "
"not supported on this device.";
@@ -171,7 +175,10 @@
TEST(record_cmd, post_unwind_option) {
if (IsDwarfCallChainSamplingSupported()) {
- ASSERT_TRUE(RunRecordCmd({"--call-graph", "dwarf", "--post-unwind"}));
+ std::vector<std::unique_ptr<Workload>> workloads;
+ CreateProcesses(1, &workloads);
+ std::string pid = std::to_string(workloads[0]->GetPid());
+ ASSERT_TRUE(RunRecordCmd({"-p", pid, "--call-graph", "dwarf", "--post-unwind"}));
} else {
GTEST_LOG_(INFO) << "This test does nothing as dwarf callchain sampling is "
"not supported on this device.";
@@ -290,11 +297,14 @@
CheckDsoSymbolRecords(tmpfile.path, true, &success);
ASSERT_TRUE(success);
if (IsDwarfCallChainSamplingSupported()) {
- ASSERT_TRUE(RunRecordCmd({"-g"}, tmpfile.path));
+ std::vector<std::unique_ptr<Workload>> workloads;
+ CreateProcesses(1, &workloads);
+ std::string pid = std::to_string(workloads[0]->GetPid());
+ ASSERT_TRUE(RunRecordCmd({"-p", pid, "-g"}, tmpfile.path));
bool success;
CheckDsoSymbolRecords(tmpfile.path, false, &success);
ASSERT_TRUE(success);
- ASSERT_TRUE(RunRecordCmd({"-g", "--dump-symbols"}, tmpfile.path));
+ ASSERT_TRUE(RunRecordCmd({"-p", pid, "-g", "--dump-symbols"}, tmpfile.path));
CheckDsoSymbolRecords(tmpfile.path, true, &success);
ASSERT_TRUE(success);
}
diff --git a/simpleperf/cmd_report_test.cpp b/simpleperf/cmd_report_test.cpp
index 98190ee..f34be5c 100644
--- a/simpleperf/cmd_report_test.cpp
+++ b/simpleperf/cmd_report_test.cpp
@@ -446,9 +446,12 @@
TEST_F(ReportCommandTest, dwarf_callgraph) {
if (IsDwarfCallChainSamplingSupported()) {
+ std::vector<std::unique_ptr<Workload>> workloads;
+ CreateProcesses(1, &workloads);
+ std::string pid = std::to_string(workloads[0]->GetPid());
TemporaryFile tmp_file;
ASSERT_TRUE(
- RecordCmd()->Run({"-g", "-o", tmp_file.path, "sleep", SLEEP_SEC}));
+ RecordCmd()->Run({"-p", pid, "-g", "-o", tmp_file.path, "sleep", SLEEP_SEC}));
ReportRaw(tmp_file.path, {"-g"});
ASSERT_TRUE(success);
} else {
diff --git a/simpleperf/cmd_stat_test.cpp b/simpleperf/cmd_stat_test.cpp
index 25fcaf9..125f938 100644
--- a/simpleperf/cmd_stat_test.cpp
+++ b/simpleperf/cmd_stat_test.cpp
@@ -55,9 +55,15 @@
void CreateProcesses(size_t count,
std::vector<std::unique_ptr<Workload>>* workloads) {
workloads->clear();
+ // Create workloads run longer than profiling time.
+ auto function = []() {
+ while (true) {
+ for (volatile int i = 0; i < 10000; ++i);
+ usleep(1);
+ }
+ };
for (size_t i = 0; i < count; ++i) {
- // Create a workload runs longer than profiling time.
- auto workload = Workload::CreateWorkload({"sleep", "1000"});
+ auto workload = Workload::CreateWorkload(function);
ASSERT_TRUE(workload != nullptr);
ASSERT_TRUE(workload->Start());
workloads->push_back(std::move(workload));
diff --git a/simpleperf/dso.cpp b/simpleperf/dso.cpp
index c889416..d52c445 100644
--- a/simpleperf/dso.cpp
+++ b/simpleperf/dso.cpp
@@ -118,14 +118,18 @@
build_id_map_ = std::move(map);
}
-BuildId Dso::GetExpectedBuildId() {
- auto it = build_id_map_.find(path_);
+BuildId Dso::FindExpectedBuildIdForPath(const std::string& path) {
+ auto it = build_id_map_.find(path);
if (it != build_id_map_.end()) {
return it->second;
}
return BuildId();
}
+BuildId Dso::GetExpectedBuildId() {
+ return FindExpectedBuildIdForPath(path_);
+}
+
std::unique_ptr<Dso> Dso::CreateDso(DsoType dso_type,
const std::string& dso_path) {
return std::unique_ptr<Dso>(new Dso(dso_type, dso_path));
diff --git a/simpleperf/dso.h b/simpleperf/dso.h
index 17b3988..7102f0e 100644
--- a/simpleperf/dso.h
+++ b/simpleperf/dso.h
@@ -92,6 +92,7 @@
}
static void SetBuildIds(
const std::vector<std::pair<std::string, BuildId>>& build_ids);
+ static BuildId FindExpectedBuildIdForPath(const std::string& path);
static std::unique_ptr<Dso> CreateDso(DsoType dso_type,
const std::string& dso_path);
diff --git a/simpleperf/dwarf_unwind.cpp b/simpleperf/dwarf_unwind.cpp
index 5444ef9..5f437c3 100644
--- a/simpleperf/dwarf_unwind.cpp
+++ b/simpleperf/dwarf_unwind.cpp
@@ -94,10 +94,13 @@
return ucontext;
}
-std::vector<uint64_t> UnwindCallChain(ArchType arch, const ThreadEntry& thread,
+std::vector<uint64_t> UnwindCallChain(int abi, const ThreadEntry& thread,
const RegSet& regs, const char* stack,
size_t stack_size, bool strict_arch_check) {
std::vector<uint64_t> result;
+ ArchType arch = (abi != PERF_SAMPLE_REGS_ABI_32) ?
+ ScopedCurrentArch::GetCurrentArch() :
+ ScopedCurrentArch::GetCurrentArch32();
if (!IsArchTheSame(arch, GetBuildArch(), strict_arch_check)) {
LOG(FATAL) << "simpleperf is built in arch " << GetArchString(GetBuildArch())
<< ", and can't do stack unwinding for arch " << GetArchString(arch);
diff --git a/simpleperf/dwarf_unwind.h b/simpleperf/dwarf_unwind.h
index 2a28a9e..e6fd8d3 100644
--- a/simpleperf/dwarf_unwind.h
+++ b/simpleperf/dwarf_unwind.h
@@ -27,7 +27,7 @@
using ThreadEntry = simpleperf::ThreadEntry;
-std::vector<uint64_t> UnwindCallChain(ArchType arch, const ThreadEntry& thread, const RegSet& regs,
+std::vector<uint64_t> UnwindCallChain(int abi, const ThreadEntry& thread, const RegSet& regs,
const char* stack, size_t stack_size, bool strict_arch_check);
#endif // SIMPLE_PERF_DWARF_UNWIND_H_
diff --git a/simpleperf/environment.cpp b/simpleperf/environment.cpp
index 373f3e0..3859de1 100644
--- a/simpleperf/environment.cpp
+++ b/simpleperf/environment.cpp
@@ -31,6 +31,7 @@
#include <android-base/parseint.h>
#include <android-base/strings.h>
#include <android-base/stringprintf.h>
+#include <procinfo/process.h>
#if defined(__ANDROID__)
#include <sys/system_properties.h>
@@ -218,46 +219,22 @@
}
static bool ReadThreadNameAndPid(pid_t tid, std::string* comm, pid_t* pid) {
- std::string status_file = android::base::StringPrintf("/proc/%d/status", tid);
- FILE* fp = fopen(status_file.c_str(), "re");
- if (fp == nullptr) {
+ android::procinfo::ProcessInfo procinfo;
+ if (!android::procinfo::GetProcessInfo(tid, &procinfo)) {
return false;
}
- bool read_comm = false;
- bool read_tgid = false;
- LineReader reader(fp);
- char* line;
- while ((line = reader.ReadLine()) != nullptr) {
- char s[reader.MaxLineSize()];
- pid_t tgid;
- if (sscanf(line, "Name:%s", s) == 1) {
- read_comm = true;
- if (comm != nullptr) {
- *comm = s;
- }
- } else if (sscanf(line, "Tgid:%d", &tgid) == 1) {
- read_tgid = true;
- if (pid != nullptr) {
- *pid = tgid;
- }
- }
- if (read_comm && read_tgid) {
- return true;
- }
+ if (comm != nullptr) {
+ *comm = procinfo.name;
}
- return false;
+ if (pid != nullptr) {
+ *pid = procinfo.pid;
+ }
+ return true;
}
std::vector<pid_t> GetThreadsInProcess(pid_t pid) {
std::vector<pid_t> result;
- std::string task_dirname = android::base::StringPrintf("/proc/%d/task", pid);
- for (const auto& name : GetSubDirs(task_dirname)) {
- int tid;
- if (!android::base::ParseInt(name.c_str(), &tid, 0)) {
- continue;
- }
- result.push_back(tid);
- }
+ android::procinfo::GetProcessTids(pid, &result);
return result;
}
@@ -474,3 +451,16 @@
<< "to fix this.";
return false;
}
+
+ArchType GetMachineArch() {
+ utsname uname_buf;
+ if (TEMP_FAILURE_RETRY(uname(&uname_buf)) != 0) {
+ PLOG(WARNING) << "uname() failed";
+ return GetBuildArch();
+ }
+ ArchType arch = GetArchType(uname_buf.machine);
+ if (arch != ARCH_UNSUPPORTED) {
+ return arch;
+ }
+ return GetBuildArch();
+}
diff --git a/simpleperf/environment.h b/simpleperf/environment.h
index 8980b07..3baaf75 100644
--- a/simpleperf/environment.h
+++ b/simpleperf/environment.h
@@ -26,6 +26,7 @@
#include <vector>
#include "build_id.h"
+#include "perf_regs.h"
std::vector<int> GetOnlineCpus();
std::vector<int> GetCpusFromString(const std::string& s);
@@ -74,4 +75,6 @@
}
#endif
+ArchType GetMachineArch();
+
#endif // SIMPLE_PERF_ENVIRONMENT_H_
diff --git a/simpleperf/event_selection_set.cpp b/simpleperf/event_selection_set.cpp
index 58c5129..6c45f15 100644
--- a/simpleperf/event_selection_set.cpp
+++ b/simpleperf/event_selection_set.cpp
@@ -280,7 +280,7 @@
PERF_SAMPLE_STACK_USER;
selection.event_attr.exclude_callchain_user = 1;
selection.event_attr.sample_regs_user =
- GetSupportedRegMask(GetBuildArch());
+ GetSupportedRegMask(GetMachineArch());
selection.event_attr.sample_stack_user = dump_stack_size;
}
}
diff --git a/simpleperf/nonlinux_support/nonlinux_support.cpp b/simpleperf/nonlinux_support/nonlinux_support.cpp
index 58c1ba0..f132dd9 100644
--- a/simpleperf/nonlinux_support/nonlinux_support.cpp
+++ b/simpleperf/nonlinux_support/nonlinux_support.cpp
@@ -20,7 +20,7 @@
#include "dwarf_unwind.h"
#include "environment.h"
-std::vector<uint64_t> UnwindCallChain(ArchType, const ThreadEntry&, const RegSet&,
+std::vector<uint64_t> UnwindCallChain(int, const ThreadEntry&, const RegSet&,
const char*, size_t, bool) {
return std::vector<uint64_t>();
}
diff --git a/simpleperf/perf_regs.cpp b/simpleperf/perf_regs.cpp
index 0b15398..d27b62a 100644
--- a/simpleperf/perf_regs.cpp
+++ b/simpleperf/perf_regs.cpp
@@ -23,7 +23,8 @@
#include "perf_event.h"
-ArchType ScopedCurrentArch::current_arch = GetBuildArch();
+ArchType ScopedCurrentArch::current_arch = ARCH_UNSUPPORTED;
+ArchType ScopedCurrentArch::current_arch32 = ARCH_UNSUPPORTED;
ArchType GetArchType(const std::string& arch) {
if (arch == "x86" || arch == "i686") {
@@ -33,6 +34,15 @@
} else if (arch == "aarch64") {
return ARCH_ARM64;
} else if (android::base::StartsWith(arch, "arm")) {
+ // If arch is "armv8l", it is likely that we are using a 32-bit simpleperf
+ // binary on a aarch64 device. In this case, the profiling environment is
+ // ARCH_ARM64, because the kernel is aarch64.
+ if (arch[3] == 'v') {
+ int version = atoi(&arch[4]);
+ if (version >= 8) {
+ return ARCH_ARM64;
+ }
+ }
return ARCH_ARM;
}
LOG(ERROR) << "unsupported arch: " << arch;
@@ -156,7 +166,7 @@
}
}
-RegSet CreateRegSet(uint64_t valid_mask, const uint64_t* valid_regs) {
+RegSet CreateRegSet(int abi, uint64_t valid_mask, const uint64_t* valid_regs) {
RegSet regs;
regs.valid_mask = valid_mask;
for (int i = 0, j = 0; i < 64; ++i) {
@@ -164,9 +174,35 @@
regs.data[i] = valid_regs[j++];
}
}
+ if (ScopedCurrentArch::GetCurrentArch() == ARCH_ARM64 &&
+ abi == PERF_SAMPLE_REGS_ABI_32) {
+ // The kernel dumps arm64 regs, but we need arm regs. So map arm64
+ // regs into arm regs.
+ regs.data[PERF_REG_ARM_PC] = regs.data[PERF_REG_ARM64_PC];
+ }
return regs;
}
+void SetIpReg(ArchType arch, uint64_t ip, RegSet* regs) {
+ int regno;
+ switch (arch) {
+ case ARCH_X86_64:
+ case ARCH_X86_32:
+ regno = PERF_REG_X86_IP;
+ break;
+ case ARCH_ARM:
+ regno = PERF_REG_ARM_PC;
+ break;
+ case ARCH_ARM64:
+ regno = PERF_REG_ARM64_PC;
+ break;
+ default:
+ return;
+ }
+ regs->valid_mask |= (1ULL << regno);
+ regs->data[regno] = ip;
+}
+
bool GetRegValue(const RegSet& regs, size_t regno, uint64_t* value) {
CHECK_LT(regno, 64U);
if ((regs.valid_mask >> regno) & 1) {
diff --git a/simpleperf/perf_regs.h b/simpleperf/perf_regs.h
index ff13d4f..fd88de6 100644
--- a/simpleperf/perf_regs.h
+++ b/simpleperf/perf_regs.h
@@ -33,6 +33,8 @@
#include <string>
#include <vector>
+#include "perf_event.h"
+
enum ArchType {
ARCH_X86_32,
ARCH_X86_64,
@@ -66,17 +68,23 @@
public:
explicit ScopedCurrentArch(ArchType arch) : saved_arch(current_arch) {
current_arch = arch;
+ current_arch32 = GetArchForAbi(arch, PERF_SAMPLE_REGS_ABI_32);
}
~ScopedCurrentArch() {
current_arch = saved_arch;
+ current_arch32 = GetArchForAbi(saved_arch, PERF_SAMPLE_REGS_ABI_32);
}
static ArchType GetCurrentArch() {
return current_arch;
}
+ static ArchType GetCurrentArch32() {
+ return current_arch32;
+ }
private:
ArchType saved_arch;
static ArchType current_arch;
+ static ArchType current_arch32;
};
struct RegSet {
@@ -84,7 +92,7 @@
uint64_t data[64];
};
-RegSet CreateRegSet(uint64_t valid_mask, const uint64_t* valid_regs);
+RegSet CreateRegSet(int abi, uint64_t valid_mask, const uint64_t* valid_regs);
bool GetRegValue(const RegSet& regs, size_t regno, uint64_t* value);
bool GetSpRegValue(const RegSet& regs, ArchType arch, uint64_t* value);
diff --git a/simpleperf/read_elf.cpp b/simpleperf/read_elf.cpp
index 8f2f0b2..7e919a9 100644
--- a/simpleperf/read_elf.cpp
+++ b/simpleperf/read_elf.cpp
@@ -149,8 +149,7 @@
if (it->getContents(data)) {
return ElfStatus::READ_FAILED;
}
- if (GetBuildIdFromNoteSection(reinterpret_cast<const char*>(data.data()),
- data.size(), build_id)) {
+ if (GetBuildIdFromNoteSection(data.data(), data.size(), build_id)) {
return ElfStatus::NO_ERROR;
}
}
diff --git a/simpleperf/record_file_writer.cpp b/simpleperf/record_file_writer.cpp
index 802a66d..fe62ab5 100644
--- a/simpleperf/record_file_writer.cpp
+++ b/simpleperf/record_file_writer.cpp
@@ -355,7 +355,8 @@
}
bool RecordFileWriter::EndWriteFeatures() {
- CHECK_LE(feature_count_, features_.size());
+ // Used features (features_.size()) should be <= allocated feature space.
+ CHECK_LE(features_.size(), feature_count_);
if (fseek(record_fp_, feature_section_offset_, SEEK_SET) == -1) {
PLOG(ERROR) << "fseek() failed";
return false;
diff --git a/simpleperf/report_lib_interface.cpp b/simpleperf/report_lib_interface.cpp
index cdd9fb1..abd0962 100644
--- a/simpleperf/report_lib_interface.cpp
+++ b/simpleperf/report_lib_interface.cpp
@@ -15,8 +15,10 @@
*/
#include <memory>
+#include <utility>
#include <android-base/logging.h>
+#include <android-base/file.h>
#include "dso.h"
#include "event_attr.h"
@@ -49,6 +51,7 @@
const char* dso_name;
uint64_t vaddr_in_file;
const char* symbol_name;
+ uint64_t symbol_addr;
};
struct CallChainEntry {
@@ -71,12 +74,15 @@
bool SetLogSeverity(ReportLib* report_lib, const char* log_level) EXPORT;
bool SetSymfs(ReportLib* report_lib, const char* symfs_dir) EXPORT;
bool SetRecordFile(ReportLib* report_lib, const char* record_file) EXPORT;
+bool SetKallsymsFile(ReportLib* report_lib, const char* kallsyms_file) EXPORT;
void ShowIpForUnknownSymbol(ReportLib* report_lib) EXPORT;
Sample* GetNextSample(ReportLib* report_lib) EXPORT;
Event* GetEventOfCurrentSample(ReportLib* report_lib) EXPORT;
SymbolEntry* GetSymbolOfCurrentSample(ReportLib* report_lib) EXPORT;
CallChain* GetCallChainOfCurrentSample(ReportLib* report_lib) EXPORT;
+
+const char* GetBuildIdForPath(ReportLib* report_lib, const char* path) EXPORT;
}
struct EventAttrWithName {
@@ -110,6 +116,8 @@
return true;
}
+ bool SetKallsymsFile(const char* kallsyms_file);
+
void ShowIpForUnknownSymbol() { thread_tree_.ShowIpForUnknownSymbol(); }
Sample* GetNextSample();
@@ -117,8 +125,11 @@
SymbolEntry* GetSymbolOfCurrentSample();
CallChain* GetCallChainOfCurrentSample();
+ const char* GetBuildIdForPath(const char* path);
+
private:
Sample* GetCurrentSample();
+ bool OpenRecordFileIfNecessary();
std::unique_ptr<android::base::ScopedLogSeverity> log_severity_;
std::string record_filename_;
@@ -131,6 +142,7 @@
SymbolEntry current_symbol_;
CallChain current_callchain_;
std::vector<CallChainEntry> callchain_entries_;
+ std::string build_id_string_;
int update_flag_;
std::vector<EventAttrWithName> event_attrs_;
};
@@ -146,14 +158,31 @@
return true;
}
-Sample* ReportLib::GetNextSample() {
+bool ReportLib::SetKallsymsFile(const char* kallsyms_file) {
+ std::string kallsyms;
+ if (!android::base::ReadFileToString(kallsyms_file, &kallsyms)) {
+ LOG(WARNING) << "Failed to read in kallsyms file from " << kallsyms_file;
+ return false;
+ }
+ Dso::SetKallsyms(std::move(kallsyms));
+ return true;
+}
+
+bool ReportLib::OpenRecordFileIfNecessary() {
if (record_file_reader_ == nullptr) {
record_file_reader_ = RecordFileReader::CreateInstance(record_filename_);
if (record_file_reader_ == nullptr) {
- return nullptr;
+ return false;
}
record_file_reader_->LoadBuildIdAndFileFeatures(thread_tree_);
}
+ return true;
+}
+
+Sample* ReportLib::GetNextSample() {
+ if (!OpenRecordFileIfNecessary()) {
+ return nullptr;
+ }
while (true) {
std::unique_ptr<Record> record;
if (!record_file_reader_->ReadRecord(record)) {
@@ -220,6 +249,7 @@
current_symbol_.dso_name = map->dso->Path().c_str();
current_symbol_.vaddr_in_file = vaddr_in_file;
current_symbol_.symbol_name = symbol->DemangledName();
+ current_symbol_.symbol_addr = symbol->addr;
update_flag_ |= UPDATE_FLAG_OF_SYMBOL;
}
return ¤t_symbol_;
@@ -265,6 +295,7 @@
entry.symbol.dso_name = map->dso->Path().c_str();
entry.symbol.vaddr_in_file = vaddr_in_file;
entry.symbol.symbol_name = symbol->DemangledName();
+ entry.symbol.symbol_addr = symbol->addr;
callchain_entries_.push_back(entry);
}
}
@@ -276,6 +307,20 @@
return ¤t_callchain_;
}
+const char* ReportLib::GetBuildIdForPath(const char* path) {
+ if (!OpenRecordFileIfNecessary()) {
+ build_id_string_.clear();
+ return build_id_string_.c_str();
+ }
+ BuildId build_id = Dso::FindExpectedBuildIdForPath(path);
+ if (build_id.IsEmpty()) {
+ build_id_string_.clear();
+ } else {
+ build_id_string_ = build_id.ToString();
+ }
+ return build_id_string_.c_str();
+}
+
// Exported methods working with a client created instance
ReportLib* CreateReportLib() {
return new ReportLib();
@@ -301,6 +346,10 @@
return report_lib->ShowIpForUnknownSymbol();
}
+bool SetKallsymsFile(ReportLib* report_lib, const char* kallsyms_file) {
+ return report_lib->SetKallsymsFile(kallsyms_file);
+}
+
Sample* GetNextSample(ReportLib* report_lib) {
return report_lib->GetNextSample();
}
@@ -316,3 +365,7 @@
CallChain* GetCallChainOfCurrentSample(ReportLib* report_lib) {
return report_lib->GetCallChainOfCurrentSample();
}
+
+const char* GetBuildIdForPath(ReportLib* report_lib, const char* path) {
+ return report_lib->GetBuildIdForPath(path);
+}
diff --git a/simpleperf/sample_tree.h b/simpleperf/sample_tree.h
index 7091ce0..ba6bbbe 100644
--- a/simpleperf/sample_tree.h
+++ b/simpleperf/sample_tree.h
@@ -106,12 +106,12 @@
(r.regs_user_data.reg_mask != 0) &&
(r.sample_type & PERF_SAMPLE_STACK_USER) &&
(r.GetValidStackSize() > 0)) {
- RegSet regs =
- CreateRegSet(r.regs_user_data.reg_mask, r.regs_user_data.regs);
- ArchType arch = GetArchForAbi(ScopedCurrentArch::GetCurrentArch(),
- r.regs_user_data.abi);
+ RegSet regs = CreateRegSet(r.regs_user_data.abi,
+ r.regs_user_data.reg_mask,
+ r.regs_user_data.regs);
std::vector<uint64_t> unwind_ips =
- UnwindCallChain(arch, *thread, regs, r.stack_user_data.data,
+ UnwindCallChain(r.regs_user_data.abi, *thread, regs,
+ r.stack_user_data.data,
r.GetValidStackSize(), strict_unwind_arch_check_);
if (!unwind_ips.empty()) {
ips.push_back(PERF_CONTEXT_USER);
diff --git a/simpleperf/scripts/libsimpleperf_report.so b/simpleperf/scripts/libsimpleperf_report.so
index a31c8e5..8682534 100755
--- a/simpleperf/scripts/libsimpleperf_report.so
+++ b/simpleperf/scripts/libsimpleperf_report.so
Binary files differ
diff --git a/simpleperf/scripts/report_sample.py b/simpleperf/scripts/report_sample.py
index 831baeb..0da3797 100644
--- a/simpleperf/scripts/report_sample.py
+++ b/simpleperf/scripts/report_sample.py
@@ -18,18 +18,20 @@
"""report_sample.py: report samples in the same format as `perf script`.
"""
+from __future__ import print_function
import sys
from simpleperf_report_lib import *
def usage():
- print 'python report_sample.py [options] <record_file>'
- print '-h/--help print this help message'
- print '--symfs <symfs_dir> Set the path to looking for symbols'
- print 'If record file is not given, use default file perf.data.'
+ print('python report_sample.py [options] <record_file>')
+ print('-h/--help print this help message')
+ print('--symfs <symfs_dir> Set the path to looking for symbols')
+ print('--kallsyms <kallsyms_file> Set the path to a kallsyms file')
+ print('If record file is not given, use default file perf.data.')
-def report_sample(record_file, symfs_dir):
+def report_sample(record_file, symfs_dir, kallsyms_file=None):
""" read record_file, and print each sample"""
lib = ReportLib()
@@ -38,6 +40,8 @@
lib.SetSymfs(symfs_dir)
if record_file is not None:
lib.SetRecordFile(record_file)
+ if kallsyms_file is not None:
+ lib.SetKallsymsFile(kallsyms_file)
while True:
sample = lib.GetNextSample()
@@ -48,19 +52,22 @@
symbol = lib.GetSymbolOfCurrentSample()
callchain = lib.GetCallChainOfCurrentSample()
- sec = sample[0].time / 1000000000
- usec = (sample[0].time - sec * 1000000000) / 1000
- print '%s\t%d [%03d] %d.%d:\t\t%d %s:' % (sample[0].thread_comm, sample[0].tid, sample[0].cpu, sec, usec, sample[0].period, event[0].name)
- print '%16x\t%s (%s)' % (sample[0].ip, symbol[0].symbol_name, symbol[0].dso_name)
- for i in range(callchain[0].nr):
- entry = callchain[0].entries[i]
- print '%16x\t%s (%s)' % (entry.ip, entry.symbol.symbol_name, entry.symbol.dso_name)
- print
+ sec = sample.time / 1000000000
+ usec = (sample.time - sec * 1000000000) / 1000
+ print('%s\t%d [%03d] %d.%d:\t\t%d %s:' % (sample.thread_comm,
+ sample.tid, sample.cpu, sec,
+ usec, sample.period, event.name))
+ print('%16x\t%s (%s)' % (sample.ip, symbol.symbol_name, symbol.dso_name))
+ for i in range(callchain.nr):
+ entry = callchain.entries[i]
+ print('%16x\t%s (%s)' % (entry.ip, entry.symbol.symbol_name, entry.symbol.dso_name))
+ print('')
if __name__ == '__main__':
record_file = 'perf.data'
symfs_dir = None
+ kallsyms_file = None
i = 1
while i < len(sys.argv):
if sys.argv[i] == '-h' or sys.argv[i] == '--help':
@@ -71,10 +78,17 @@
symfs_dir = sys.argv[i + 1]
i += 1
else:
- print 'argument for --symfs is missing'
+ print('argument for --symfs is missing')
+ sys.exit(1)
+ elif sys.argv[i] == '--kallsyms':
+ if i + 1 < len(sys.argv):
+ kallsyms_file = sys.argv[i + 1]
+ i += 1
+ else:
+ print('argument for --kallsyms is missing')
sys.exit(1)
else:
record_file = sys.argv[i]
i += 1
- report_sample(record_file, symfs_dir)
+ report_sample(record_file, symfs_dir, kallsyms_file)
diff --git a/simpleperf/scripts/simpleperf_report_lib.py b/simpleperf/scripts/simpleperf_report_lib.py
index 8b0e133..46264be 100644
--- a/simpleperf/scripts/simpleperf_report_lib.py
+++ b/simpleperf/scripts/simpleperf_report_lib.py
@@ -21,16 +21,48 @@
import ctypes as ct
import os
+import subprocess
+import sys
+import unittest
-def _get_script_path():
+def _isWindows():
+ return sys.platform == 'win32' or sys.platform == 'cygwin'
+
+
+def _get_script_dir():
return os.path.dirname(os.path.realpath(__file__))
+def _get_native_lib():
+ if _isWindows():
+ so_name = 'libsimpleperf_report.dll'
+ elif sys.platform == 'darwin': # OSX
+ so_name = 'libsimpleperf_report.dylib'
+ else:
+ so_name = 'libsimpleperf_report.so'
+
+ return os.path.join(_get_script_dir(), so_name)
+
+
def _is_null(p):
return ct.cast(p, ct.c_void_p).value is None
+def _char_pt(str):
+ if sys.version_info < (3, 0):
+ return str
+ # In python 3, str are wide strings whereas the C api expects 8 bit strings, hence we have to convert
+ # For now using utf-8 as the encoding.
+ return str.encode('utf-8')
+
+
+def _char_pt_to_str(char_pt):
+ if sys.version_info < (3, 0):
+ return char_pt
+ return char_pt.decode('utf-8')
+
+
class SampleStruct(ct.Structure):
_fields_ = [('ip', ct.c_uint64),
('pid', ct.c_uint32),
@@ -49,7 +81,8 @@
class SymbolStruct(ct.Structure):
_fields_ = [('dso_name', ct.c_char_p),
('vaddr_in_file', ct.c_uint64),
- ('symbol_name', ct.c_char_p)]
+ ('symbol_name', ct.c_char_p),
+ ('symbol_addr', ct.c_uint64)]
class CallChainEntryStructure(ct.Structure):
@@ -61,14 +94,58 @@
_fields_ = [('nr', ct.c_uint32),
('entries', ct.POINTER(CallChainEntryStructure))]
+
+# convert char_p to str for python3.
+class SampleStructUsingStr(object):
+ def __init__(self, sample):
+ self.ip = sample.ip
+ self.pid = sample.pid
+ self.tid = sample.tid
+ self.thread_comm = _char_pt_to_str(sample.thread_comm)
+ self.time = sample.time
+ self.in_kernel = sample.in_kernel
+ self.cpu = sample.cpu
+ self.period = sample.period
+
+
+class EventStructUsingStr(object):
+ def __init__(self, event):
+ self.name = _char_pt_to_str(event.name)
+
+
+class SymbolStructUsingStr(object):
+ def __init__(self, symbol):
+ self.dso_name = _char_pt_to_str(symbol.dso_name)
+ self.vaddr_in_file = symbol.vaddr_in_file
+ self.symbol_name = _char_pt_to_str(symbol.symbol_name)
+ self.symbol_addr = symbol.symbol_addr
+
+
+class CallChainEntryStructureUsingStr(object):
+ def __init__(self, entry):
+ self.ip = entry.ip
+ self.symbol = SymbolStructUsingStr(entry.symbol)
+
+
+class CallChainStructureUsingStr(object):
+ def __init__(self, callchain):
+ self.nr = callchain.nr
+ self.entries = []
+ for i in range(self.nr):
+ self.entries.append(CallChainEntryStructureUsingStr(callchain.entries[i]))
+
+
class ReportLibStructure(ct.Structure):
_fields_ = []
+
class ReportLib(object):
def __init__(self, native_lib_path=None):
if native_lib_path is None:
- native_lib_path = _get_script_path() + "/libsimpleperf_report.so"
+ native_lib_path = _get_native_lib()
+
+ self._load_dependent_lib()
self._lib = ct.CDLL(native_lib_path)
self._CreateReportLibFunc = self._lib.CreateReportLib
self._CreateReportLibFunc.restype = ct.POINTER(ReportLibStructure)
@@ -76,6 +153,7 @@
self._SetLogSeverityFunc = self._lib.SetLogSeverity
self._SetSymfsFunc = self._lib.SetSymfs
self._SetRecordFileFunc = self._lib.SetRecordFile
+ self._SetKallsymsFileFunc = self._lib.SetKallsymsFile
self._ShowIpForUnknownSymbolFunc = self._lib.ShowIpForUnknownSymbol
self._GetNextSampleFunc = self._lib.GetNextSample
self._GetNextSampleFunc.restype = ct.POINTER(SampleStruct)
@@ -86,9 +164,21 @@
self._GetCallChainOfCurrentSampleFunc = self._lib.GetCallChainOfCurrentSample
self._GetCallChainOfCurrentSampleFunc.restype = ct.POINTER(
CallChainStructure)
+ self._GetBuildIdForPathFunc = self._lib.GetBuildIdForPath
+ self._GetBuildIdForPathFunc.restype = ct.c_char_p
self._instance = self._CreateReportLibFunc()
assert(not _is_null(self._instance))
+ self.convert_to_str = (sys.version_info >= (3, 0))
+
+ def _load_dependent_lib(self):
+ # As the windows dll is built with mingw we need to also find "libwinpthread-1.dll".
+ # Load it before libsimpleperf_report.dll if it does exist in the same folder as this script.
+ if _isWindows():
+ libwinpthread_path = os.path.join(_get_script_path(), "libwinpthread-1.dll")
+ if os.path.exists(libwinpthread_path):
+ self._libwinpthread = ct.CDLL(libwinpthread_path)
+
def Close(self):
if self._instance is None:
return
@@ -97,44 +187,131 @@
def SetLogSeverity(self, log_level='info'):
""" Set log severity of native lib, can be verbose,debug,info,error,fatal."""
- cond = self._SetLogSeverityFunc(self.getInstance(), log_level)
- assert(cond)
+ cond = self._SetLogSeverityFunc(self.getInstance(), _char_pt(log_level))
+ self._check(cond, "Failed to set log level")
def SetSymfs(self, symfs_dir):
""" Set directory used to find symbols."""
- cond = self._SetSymfsFunc(self.getInstance(), symfs_dir)
- assert(cond)
+ cond = self._SetSymfsFunc(self.getInstance(), _char_pt(symfs_dir))
+ self._check(cond, "Failed to set symbols directory")
def SetRecordFile(self, record_file):
""" Set the path of record file, like perf.data."""
- cond = self._SetRecordFileFunc(self.getInstance(), record_file)
- assert(cond)
+ cond = self._SetRecordFileFunc(self.getInstance(), _char_pt(record_file))
+ self._check(cond, "Failed to set record file")
def ShowIpForUnknownSymbol(self):
self._ShowIpForUnknownSymbolFunc(self.getInstance())
+ def SetKallsymsFile(self, kallsym_file):
+ """ Set the file path to a copy of the /proc/kallsyms file (for off device decoding) """
+ cond = self._SetKallsymsFileFunc(self.getInstance(), _char_pt(kallsym_file))
+ self._check(cond, "Failed to set kallsyms file")
+
def GetNextSample(self):
sample = self._GetNextSampleFunc(self.getInstance())
if _is_null(sample):
return None
- return sample
+ if self.convert_to_str:
+ return SampleStructUsingStr(sample[0])
+ return sample[0]
def GetEventOfCurrentSample(self):
event = self._GetEventOfCurrentSampleFunc(self.getInstance())
assert(not _is_null(event))
- return event
+ if self.convert_to_str:
+ return EventStructUsingStr(event[0])
+ return event[0]
def GetSymbolOfCurrentSample(self):
symbol = self._GetSymbolOfCurrentSampleFunc(self.getInstance())
assert(not _is_null(symbol))
- return symbol
+ if self.convert_to_str:
+ return SymbolStructUsingStr(symbol[0])
+ return symbol[0]
def GetCallChainOfCurrentSample(self):
callchain = self._GetCallChainOfCurrentSampleFunc(self.getInstance())
assert(not _is_null(callchain))
- return callchain
+ if self.convert_to_str:
+ return CallChainStructureUsingStr(callchain[0])
+ return callchain[0]
+
+ def GetBuildIdForPath(self, path):
+ build_id = self._GetBuildIdForPathFunc(self.getInstance(), _char_pt(path))
+ assert(not _is_null(build_id))
+ return _char_pt_to_str(build_id)
def getInstance(self):
if self._instance is None:
raise Exception("Instance is Closed")
return self._instance
+
+ def _check(self, cond, failmsg):
+ if not cond:
+ raise Exception(failmsg)
+
+
+class TestReportLib(unittest.TestCase):
+ def setUp(self):
+ self.perf_data_path = os.path.join(os.path.dirname(_get_script_dir()),
+ 'testdata', 'perf_with_symbols.data')
+ if not os.path.isfile(self.perf_data_path):
+ raise Exception("can't find perf_data at %s" % self.perf_data_path)
+ self.report_lib = ReportLib()
+ self.report_lib.SetRecordFile(self.perf_data_path)
+
+ def tearDown(self):
+ self.report_lib.Close()
+
+ def test_build_id(self):
+ build_id = self.report_lib.GetBuildIdForPath('/data/t2')
+ self.assertEqual(build_id, '0x70f1fe24500fc8b0d9eb477199ca1ca21acca4de')
+
+ def test_symbol_addr(self):
+ found_func2 = False
+ while True:
+ sample = self.report_lib.GetNextSample()
+ if sample is None:
+ break
+ symbol = self.report_lib.GetSymbolOfCurrentSample()
+ if symbol.symbol_name == 'func2(int, int)':
+ found_func2 = True
+ self.assertEqual(symbol.symbol_addr, 0x4004ed)
+ self.assertTrue(found_func2)
+
+ def test_sample(self):
+ found_sample = False
+ while True:
+ sample = self.report_lib.GetNextSample()
+ if sample is None:
+ break
+ if sample.ip == 0x4004ff and sample.time == 7637889424953:
+ found_sample = True
+ self.assertEqual(sample.pid, 15926)
+ self.assertEqual(sample.tid, 15926)
+ self.assertEqual(sample.thread_comm, 't2')
+ self.assertEqual(sample.cpu, 5)
+ self.assertEqual(sample.period, 694614)
+ event = self.report_lib.GetEventOfCurrentSample()
+ self.assertEqual(event.name, 'cpu-cycles')
+ callchain = self.report_lib.GetCallChainOfCurrentSample()
+ self.assertEqual(callchain.nr, 0)
+ self.assertTrue(found_sample)
+
+
+def main():
+ test_all = True
+ if len(sys.argv) > 1 and sys.argv[1] == '--test-one':
+ test_all = False
+ del sys.argv[1]
+
+ if test_all:
+ subprocess.check_call(['python', os.path.realpath(__file__), '--test-one'])
+ subprocess.check_call(['python3', os.path.realpath(__file__), '--test-one'])
+ else:
+ sys.exit(unittest.main())
+
+
+if __name__ == '__main__':
+ main()
\ No newline at end of file
diff --git a/simpleperf/thread_tree.cpp b/simpleperf/thread_tree.cpp
index 84cfb59..09b0e1f 100644
--- a/simpleperf/thread_tree.cpp
+++ b/simpleperf/thread_tree.cpp
@@ -103,7 +103,8 @@
}
Dso* ThreadTree::FindKernelDsoOrNew(const std::string& filename) {
- if (filename == DEFAULT_KERNEL_MMAP_NAME) {
+ if (filename == DEFAULT_KERNEL_MMAP_NAME ||
+ filename == DEFAULT_KERNEL_MMAP_NAME_PERF) {
return kernel_dso_.get();
}
auto it = module_dso_tree_.find(filename);
diff --git a/simpleperf/thread_tree.h b/simpleperf/thread_tree.h
index 3108fe4..d7566d4 100644
--- a/simpleperf/thread_tree.h
+++ b/simpleperf/thread_tree.h
@@ -29,7 +29,8 @@
struct Record;
constexpr char DEFAULT_KERNEL_MMAP_NAME[] = "[kernel.kallsyms]";
-
+// Seen in perf.data file generated by perf.
+constexpr char DEFAULT_KERNEL_MMAP_NAME_PERF[] = "[kernel.kallsyms]_text";
constexpr char DEFAULT_EXECNAME_FOR_THREAD_MMAP[] = "//anon";
namespace simpleperf {
diff --git a/simpleperf/workload.cpp b/simpleperf/workload.cpp
index 1d34c11..dcb0e78 100644
--- a/simpleperf/workload.cpp
+++ b/simpleperf/workload.cpp
@@ -25,7 +25,15 @@
#include <android-base/logging.h>
std::unique_ptr<Workload> Workload::CreateWorkload(const std::vector<std::string>& args) {
- std::unique_ptr<Workload> workload(new Workload(args));
+ std::unique_ptr<Workload> workload(new Workload(args, std::function<void ()>()));
+ if (workload != nullptr && workload->CreateNewProcess()) {
+ return workload;
+ }
+ return nullptr;
+}
+
+std::unique_ptr<Workload> Workload::CreateWorkload(const std::function<void ()>& function) {
+ std::unique_ptr<Workload> workload(new Workload(std::vector<std::string>(), function));
if (workload != nullptr && workload->CreateNewProcess()) {
return workload;
}
@@ -47,8 +55,6 @@
}
}
-static void ChildProcessFn(std::vector<std::string>& args, int start_signal_fd, int exec_child_fd);
-
bool Workload::CreateNewProcess() {
CHECK_EQ(work_state_, NotYetCreateNewProcess);
@@ -78,7 +84,7 @@
// In child process.
close(start_signal_pipe[1]);
close(exec_child_pipe[0]);
- ChildProcessFn(args_, start_signal_pipe[0], exec_child_pipe[1]);
+ ChildProcessFn(start_signal_pipe[0], exec_child_pipe[1]);
_exit(0);
}
// In parent process.
@@ -91,28 +97,33 @@
return true;
}
-static void ChildProcessFn(std::vector<std::string>& args, int start_signal_fd, int exec_child_fd) {
+void Workload::ChildProcessFn(int start_signal_fd, int exec_child_fd) {
// Die if parent exits.
prctl(PR_SET_PDEATHSIG, SIGHUP, 0, 0, 0);
- std::vector<char*> argv(args.size() + 1);
- for (size_t i = 0; i < args.size(); ++i) {
- argv[i] = &args[i][0];
- }
- argv[args.size()] = nullptr;
char start_signal = 0;
ssize_t nread = TEMP_FAILURE_RETRY(read(start_signal_fd, &start_signal, 1));
if (nread == 1 && start_signal == 1) {
close(start_signal_fd);
- execvp(argv[0], argv.data());
- // If execvp() succeed, we will not arrive here. But if it failed, we need to
- // report the failure to the parent process by writing 1 to exec_child_fd.
- int saved_errno = errno;
- char exec_child_failed = 1;
- TEMP_FAILURE_RETRY(write(exec_child_fd, &exec_child_failed, 1));
- close(exec_child_fd);
- errno = saved_errno;
- PLOG(ERROR) << "child process failed to execvp(" << argv[0] << ")";
+ if (child_proc_function_) {
+ close(exec_child_fd);
+ child_proc_function_();
+ } else {
+ char* argv[child_proc_args_.size() + 1];
+ for (size_t i = 0; i < child_proc_args_.size(); ++i) {
+ argv[i] = &child_proc_args_[i][0];
+ }
+ argv[child_proc_args_.size()] = nullptr;
+ execvp(argv[0], argv);
+ // If execvp() succeed, we will not arrive here. But if it failed, we need to
+ // report the failure to the parent process by writing 1 to exec_child_fd.
+ int saved_errno = errno;
+ char exec_child_failed = 1;
+ TEMP_FAILURE_RETRY(write(exec_child_fd, &exec_child_failed, 1));
+ close(exec_child_fd);
+ errno = saved_errno;
+ PLOG(ERROR) << "child process failed to execvp(" << argv[0] << ")";
+ }
} else {
PLOG(ERROR) << "child process failed to receive start_signal, nread = " << nread;
}
diff --git a/simpleperf/workload.h b/simpleperf/workload.h
index fa754b5..2141830 100644
--- a/simpleperf/workload.h
+++ b/simpleperf/workload.h
@@ -19,6 +19,7 @@
#include <sys/types.h>
#include <chrono>
+#include <functional>
#include <string>
#include <vector>
@@ -34,6 +35,7 @@
public:
static std::unique_ptr<Workload> CreateWorkload(const std::vector<std::string>& args);
+ static std::unique_ptr<Workload> CreateWorkload(const std::function<void ()>& function);
~Workload();
@@ -43,19 +45,24 @@
}
private:
- explicit Workload(const std::vector<std::string>& args)
+ explicit Workload(const std::vector<std::string>& args,
+ const std::function<void ()>& function)
: work_state_(NotYetCreateNewProcess),
- args_(args),
+ child_proc_args_(args),
+ child_proc_function_(function),
work_pid_(-1),
start_signal_fd_(-1),
exec_child_fd_(-1) {
}
bool CreateNewProcess();
+ void ChildProcessFn(int start_signal_fd, int exec_child_fd);
bool WaitChildProcess(bool wait_forever, bool is_child_killed);
WorkState work_state_;
- std::vector<std::string> args_;
+ // The child process either executes child_proc_args or run child_proc_function.
+ std::vector<std::string> child_proc_args_;
+ std::function<void ()> child_proc_function_;
pid_t work_pid_;
int start_signal_fd_; // The parent process writes 1 to start workload in the child process.
int exec_child_fd_; // The child process writes 1 to notify that execvp() failed.
diff --git a/tests/framebuffer/minui.h b/tests/framebuffer/minui.h
deleted file mode 100644
index 4efc971..0000000
--- a/tests/framebuffer/minui.h
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2007 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.
- */
-
-#ifndef _MINUI_H_
-#define _MINUI_H_
-
-int gr_init(void);
-void gr_exit(void);
-
-int gr_fb_width(void);
-int gr_fb_height(void);
-void gr_flip(void);
-
-void gr_color(unsigned char r, unsigned char g, unsigned char b);
-void gr_fill(int x, int y, int w, int h);
-int gr_text(int x, int y, const char *s);
-int gr_measure(const char *s);
-
-
-typedef struct event event;
-
-struct event
-{
- unsigned type;
- unsigned code;
- unsigned value;
-};
-
-int ev_init(void);
-void ev_exit(void);
-
-int ev_get(event *ev, unsigned dont_wait);
-
-#define TYPE_KEY 1
-
-#define KEY_UP 103
-#define KEY_DOWN 108
-#define KEY_LEFT 105
-#define KEY_RIGHT 106
-#define KEY_CENTER 232
-#define KEY_ENTER 28
-
-#endif
diff --git a/tests/kernel.config/Android.mk b/tests/kernel.config/Android.mk
index 0f64aed..7fa92a1 100644
--- a/tests/kernel.config/Android.mk
+++ b/tests/kernel.config/Android.mk
@@ -47,7 +47,7 @@
LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
LOCAL_STATIC_LIBRARIES := libgtest libgtest_main
-LOCAL_COMPATIBILITY_SUITE := cts_v2
+LOCAL_COMPATIBILITY_SUITE := cts
LOCAL_CTS_TEST_PACKAGE := android.kernel.config
include $(BUILD_CTS_EXECUTABLE)
diff --git a/tests/lib/testUtil/testUtil.c b/tests/lib/testUtil/testUtil.c
index d4dcea2..83dd946 100644
--- a/tests/lib/testUtil/testUtil.c
+++ b/tests/lib/testUtil/testUtil.c
@@ -25,12 +25,11 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <time.h>
-
#include <sys/time.h>
#include <sys/wait.h>
+#include <time.h>
-#include <cutils/log.h>
+#include <android/log.h>
#define ALEN(a) (sizeof(a) / sizeof((a)[0])) // Array length
typedef unsigned int bool_t;
diff --git a/tests/timetest/Android.mk b/tests/timetest/Android.mk
index 92bbf1e..b644894 100644
--- a/tests/timetest/Android.mk
+++ b/tests/timetest/Android.mk
@@ -3,20 +3,12 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= timetest.c
-
-LOCAL_MODULE:= timetest
-
-LOCAL_MODULE_TAGS := optional
-
+LOCAL_SRC_FILES := timetest.c
+LOCAL_MODULE := timetest
+LOCAL_MODULE_TAGS := tests
LOCAL_FORCE_STATIC_EXECUTABLE := true
-LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT_SBIN)
-LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_SBIN_UNSTRIPPED)
-
LOCAL_STATIC_LIBRARIES := libc
-
-include $(BUILD_EXECUTABLE)
+include $(BUILD_NATIVE_TEST)
# -----------------------------------------------------------------------------
# Unit tests.
diff --git a/verity/Android.mk b/verity/Android.mk
index ef5f08e..c4a4d2c 100644
--- a/verity/Android.mk
+++ b/verity/Android.mk
@@ -22,14 +22,6 @@
include $(BUILD_HOST_EXECUTABLE)
include $(CLEAR_VARS)
-LOCAL_SRC_FILES := VerityVerifier.java Utils.java
-LOCAL_MODULE := VerityVerifier
-LOCAL_JAR_MANIFEST := VerityVerifier.mf
-LOCAL_MODULE_TAGS := optional
-LOCAL_STATIC_JAVA_LIBRARIES := bouncycastle-host
-include $(BUILD_HOST_JAVA_LIBRARY)
-
-include $(CLEAR_VARS)
LOCAL_SRC_FILES := VeritySigner.java Utils.java
LOCAL_MODULE := VeritySigner
LOCAL_JAR_MANIFEST := VeritySigner.mf
@@ -46,13 +38,25 @@
include $(BUILD_HOST_JAVA_LIBRARY)
include $(CLEAR_VARS)
-LOCAL_SRC_FILES := verity_verifier
+LOCAL_SRC_FILES := verity_verifier.cpp
LOCAL_MODULE := verity_verifier
LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_MODULE_HOST_OS := linux
LOCAL_IS_HOST_MODULE := true
LOCAL_MODULE_TAGS := optional
-LOCAL_REQUIRED_MODULES := VerityVerifier
-include $(BUILD_PREBUILT)
+LOCAL_SANITIZE := integer
+LOCAL_STATIC_LIBRARIES := \
+ libfec \
+ libfec_rs_host \
+ libcrypto_utils \
+ libcrypto \
+ libext4_utils_host \
+ libsparse_host \
+ libsquashfs_utils_host \
+ libbase \
+ libz
+LOCAL_CFLAGS := -Wall -Werror
+include $(BUILD_HOST_EXECUTABLE)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := verity_signer
diff --git a/verity/VerityVerifier.java b/verity/VerityVerifier.java
deleted file mode 100644
index 6b3f49e..0000000
--- a/verity/VerityVerifier.java
+++ /dev/null
@@ -1,419 +0,0 @@
-/*
- * Copyright (C) 2014 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.
- */
-
-package com.android.verity;
-
-import java.io.File;
-import java.io.RandomAccessFile;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.lang.Math;
-import java.lang.Process;
-import java.lang.Runtime;
-import java.math.BigInteger;
-import java.security.KeyFactory;
-import java.security.MessageDigest;
-import java.security.PublicKey;
-import java.security.Security;
-import java.security.cert.X509Certificate;
-import java.security.interfaces.RSAPublicKey;
-import java.security.spec.RSAPublicKeySpec;
-import java.util.ArrayList;
-import java.util.Arrays;
-import javax.xml.bind.DatatypeConverter;
-import org.bouncycastle.jce.provider.BouncyCastleProvider;
-
-public class VerityVerifier {
-
- private ArrayList<Integer> hashBlocksLevel;
- private byte[] hashTree;
- private byte[] rootHash;
- private byte[] salt;
- private byte[] signature;
- private byte[] table;
- private File image;
- private int blockSize;
- private int hashBlockSize;
- private int hashOffsetForData;
- private int hashSize;
- private int hashTreeSize;
- private long hashStart;
- private long imageSize;
- private MessageDigest digest;
-
- private static final int EXT4_SB_MAGIC = 0xEF53;
- private static final int EXT4_SB_OFFSET = 0x400;
- private static final int EXT4_SB_OFFSET_MAGIC = EXT4_SB_OFFSET + 0x38;
- private static final int EXT4_SB_OFFSET_LOG_BLOCK_SIZE = EXT4_SB_OFFSET + 0x18;
- private static final int EXT4_SB_OFFSET_BLOCKS_COUNT_LO = EXT4_SB_OFFSET + 0x4;
- private static final int EXT4_SB_OFFSET_BLOCKS_COUNT_HI = EXT4_SB_OFFSET + 0x150;
- private static final int MINCRYPT_OFFSET_MODULUS = 0x8;
- private static final int MINCRYPT_OFFSET_EXPONENT = 0x208;
- private static final int MINCRYPT_MODULUS_SIZE = 0x100;
- private static final int MINCRYPT_EXPONENT_SIZE = 0x4;
- private static final int VERITY_FIELDS = 10;
- private static final int VERITY_MAGIC = 0xB001B001;
- private static final int VERITY_SIGNATURE_SIZE = 256;
- private static final int VERITY_VERSION = 0;
-
- public VerityVerifier(String fname) throws Exception {
- digest = MessageDigest.getInstance("SHA-256");
- hashSize = digest.getDigestLength();
- hashBlocksLevel = new ArrayList<Integer>();
- hashTreeSize = -1;
- openImage(fname);
- readVerityData();
- }
-
- /**
- * Reverses the order of bytes in a byte array
- * @param value Byte array to reverse
- */
- private static byte[] reverse(byte[] value) {
- for (int i = 0; i < value.length / 2; i++) {
- byte tmp = value[i];
- value[i] = value[value.length - i - 1];
- value[value.length - i - 1] = tmp;
- }
-
- return value;
- }
-
- /**
- * Converts a 4-byte little endian value to a Java integer
- * @param value Little endian integer to convert
- */
- private static int fromle(int value) {
- byte[] bytes = ByteBuffer.allocate(4).putInt(value).array();
- return ByteBuffer.wrap(bytes).order(ByteOrder.LITTLE_ENDIAN).getInt();
- }
-
- /**
- * Converts a 2-byte little endian value to Java a integer
- * @param value Little endian short to convert
- */
- private static int fromle(short value) {
- return fromle(value << 16);
- }
-
- /**
- * Reads a 2048-bit RSA public key saved in mincrypt format, and returns
- * a Java PublicKey for it.
- * @param fname Name of the mincrypt public key file
- */
- private static PublicKey getMincryptPublicKey(String fname) throws Exception {
- try (RandomAccessFile key = new RandomAccessFile(fname, "r")) {
- byte[] binaryMod = new byte[MINCRYPT_MODULUS_SIZE];
- byte[] binaryExp = new byte[MINCRYPT_EXPONENT_SIZE];
-
- key.seek(MINCRYPT_OFFSET_MODULUS);
- key.readFully(binaryMod);
-
- key.seek(MINCRYPT_OFFSET_EXPONENT);
- key.readFully(binaryExp);
-
- BigInteger modulus = new BigInteger(1, reverse(binaryMod));
- BigInteger exponent = new BigInteger(1, reverse(binaryExp));
-
- RSAPublicKeySpec spec = new RSAPublicKeySpec(modulus, exponent);
- KeyFactory factory = KeyFactory.getInstance("RSA");
- return factory.generatePublic(spec);
- }
- }
-
- /**
- * Unsparses a sparse image into a temporary file and returns a
- * handle to the file
- * @param fname Path to a sparse image file
- */
- private void openImage(String fname) throws Exception {
- image = File.createTempFile("system", ".raw");
- image.deleteOnExit();
-
- Process p = Runtime.getRuntime().exec("simg2img " + fname +
- " " + image.getAbsoluteFile());
-
- p.waitFor();
- if (p.exitValue() != 0) {
- throw new IllegalArgumentException("Invalid image: failed to unsparse");
- }
- }
-
- /**
- * Reads the ext4 superblock and calculates the size of the system image,
- * after which we should find the verity metadata
- * @param img File handle to the image file
- */
- public static long getMetadataPosition(RandomAccessFile img)
- throws Exception {
- img.seek(EXT4_SB_OFFSET_MAGIC);
- int magic = fromle(img.readShort());
-
- if (magic != EXT4_SB_MAGIC) {
- throw new IllegalArgumentException("Invalid image: not a valid ext4 image");
- }
-
- img.seek(EXT4_SB_OFFSET_BLOCKS_COUNT_LO);
- long blocksCountLo = fromle(img.readInt());
-
- img.seek(EXT4_SB_OFFSET_LOG_BLOCK_SIZE);
- long logBlockSize = fromle(img.readInt());
-
- img.seek(EXT4_SB_OFFSET_BLOCKS_COUNT_HI);
- long blocksCountHi = fromle(img.readInt());
-
- long blockSizeBytes = 1L << (10 + logBlockSize);
- long blockCount = (blocksCountHi << 32) + blocksCountLo;
- return blockSizeBytes * blockCount;
- }
-
- /**
- * Calculates the size of the verity hash tree based on the image size
- */
- private int calculateHashTreeSize() {
- if (hashTreeSize > 0) {
- return hashTreeSize;
- }
-
- int totalBlocks = 0;
- int hashes = (int) (imageSize / blockSize);
-
- hashBlocksLevel.clear();
-
- do {
- hashBlocksLevel.add(0, hashes);
-
- int hashBlocks =
- (int) Math.ceil((double) hashes * hashSize / hashBlockSize);
-
- totalBlocks += hashBlocks;
-
- hashes = hashBlocks;
- } while (hashes > 1);
-
- hashTreeSize = totalBlocks * hashBlockSize;
- return hashTreeSize;
- }
-
- /**
- * Parses the verity mapping table and reads the hash tree from
- * the image file
- * @param img Handle to the image file
- * @param table Verity mapping table
- */
- private void readHashTree(RandomAccessFile img, byte[] table)
- throws Exception {
- String tableStr = new String(table);
- String[] fields = tableStr.split(" ");
-
- if (fields.length != VERITY_FIELDS) {
- throw new IllegalArgumentException("Invalid image: unexpected number of fields "
- + "in verity mapping table (" + fields.length + ")");
- }
-
- String hashVersion = fields[0];
-
- if (!"1".equals(hashVersion)) {
- throw new IllegalArgumentException("Invalid image: unsupported hash format");
- }
-
- String alg = fields[7];
-
- if (!"sha256".equals(alg)) {
- throw new IllegalArgumentException("Invalid image: unsupported hash algorithm");
- }
-
- blockSize = Integer.parseInt(fields[3]);
- hashBlockSize = Integer.parseInt(fields[4]);
-
- int blocks = Integer.parseInt(fields[5]);
- int start = Integer.parseInt(fields[6]);
-
- if (imageSize != (long) blocks * blockSize) {
- throw new IllegalArgumentException("Invalid image: size mismatch in mapping "
- + "table");
- }
-
- rootHash = DatatypeConverter.parseHexBinary(fields[8]);
- salt = DatatypeConverter.parseHexBinary(fields[9]);
-
- hashStart = (long) start * blockSize;
- img.seek(hashStart);
-
- int treeSize = calculateHashTreeSize();
-
- hashTree = new byte[treeSize];
- img.readFully(hashTree);
- }
-
- /**
- * Reads verity data from the image file
- */
- private void readVerityData() throws Exception {
- try (RandomAccessFile img = new RandomAccessFile(image, "r")) {
- imageSize = getMetadataPosition(img);
- img.seek(imageSize);
-
- int magic = fromle(img.readInt());
-
- if (magic != VERITY_MAGIC) {
- throw new IllegalArgumentException("Invalid image: verity metadata not found");
- }
-
- int version = fromle(img.readInt());
-
- if (version != VERITY_VERSION) {
- throw new IllegalArgumentException("Invalid image: unknown metadata version");
- }
-
- signature = new byte[VERITY_SIGNATURE_SIZE];
- img.readFully(signature);
-
- int tableSize = fromle(img.readInt());
-
- table = new byte[tableSize];
- img.readFully(table);
-
- readHashTree(img, table);
- }
- }
-
- /**
- * Reads and validates verity metadata, and checks the signature against the
- * given public key
- * @param key Public key to use for signature verification
- */
- public boolean verifyMetaData(PublicKey key)
- throws Exception {
- return Utils.verify(key, table, signature,
- Utils.getSignatureAlgorithmIdentifier(key));
- }
-
- /**
- * Hashes a block of data using a salt and checks of the results are expected
- * @param hash The expected hash value
- * @param data The data block to check
- */
- private boolean checkBlock(byte[] hash, byte[] data) {
- digest.reset();
- digest.update(salt);
- digest.update(data);
- return Arrays.equals(hash, digest.digest());
- }
-
- /**
- * Verifies the root hash and the first N-1 levels of the hash tree
- */
- private boolean verifyHashTree() throws Exception {
- int hashOffset = 0;
- int dataOffset = hashBlockSize;
-
- if (!checkBlock(rootHash, Arrays.copyOfRange(hashTree, 0, hashBlockSize))) {
- System.err.println("Root hash mismatch");
- return false;
- }
-
- for (int level = 0; level < hashBlocksLevel.size() - 1; level++) {
- int blocks = hashBlocksLevel.get(level);
-
- for (int i = 0; i < blocks; i++) {
- byte[] hashBlock = Arrays.copyOfRange(hashTree,
- hashOffset + i * hashSize,
- hashOffset + i * hashSize + hashSize);
-
- byte[] dataBlock = Arrays.copyOfRange(hashTree,
- dataOffset + i * hashBlockSize,
- dataOffset + i * hashBlockSize + hashBlockSize);
-
- if (!checkBlock(hashBlock, dataBlock)) {
- System.err.printf("Hash mismatch at tree level %d, block %d\n", level, i);
- return false;
- }
- }
-
- hashOffset = dataOffset;
- hashOffsetForData = dataOffset;
- dataOffset += blocks * hashBlockSize;
- }
-
- return true;
- }
-
- /**
- * Validates the image against the hash tree
- */
- public boolean verifyData() throws Exception {
- if (!verifyHashTree()) {
- return false;
- }
-
- try (RandomAccessFile img = new RandomAccessFile(image, "r")) {
- byte[] dataBlock = new byte[blockSize];
- int hashOffset = hashOffsetForData;
-
- for (int i = 0; (long) i * blockSize < imageSize; i++) {
- byte[] hashBlock = Arrays.copyOfRange(hashTree,
- hashOffset + i * hashSize,
- hashOffset + i * hashSize + hashSize);
-
- img.readFully(dataBlock);
-
- if (!checkBlock(hashBlock, dataBlock)) {
- System.err.printf("Hash mismatch at block %d\n", i);
- return false;
- }
- }
- }
-
- return true;
- }
-
- /**
- * Verifies the integrity of the image and the verity metadata
- * @param key Public key to use for signature verification
- */
- public boolean verify(PublicKey key) throws Exception {
- return (verifyMetaData(key) && verifyData());
- }
-
- public static void main(String[] args) throws Exception {
- Security.addProvider(new BouncyCastleProvider());
- PublicKey key = null;
-
- if (args.length == 3 && "-mincrypt".equals(args[1])) {
- key = getMincryptPublicKey(args[2]);
- } else if (args.length == 2) {
- X509Certificate cert = Utils.loadPEMCertificate(args[1]);
- key = cert.getPublicKey();
- } else {
- System.err.println("Usage: VerityVerifier <sparse.img> <certificate.x509.pem> | -mincrypt <mincrypt_key>");
- System.exit(1);
- }
-
- VerityVerifier verifier = new VerityVerifier(args[0]);
-
- try {
- if (verifier.verify(key)) {
- System.err.println("Signature is VALID");
- System.exit(0);
- }
- } catch (Exception e) {
- e.printStackTrace(System.err);
- }
-
- System.exit(1);
- }
-}
diff --git a/verity/VerityVerifier.mf b/verity/VerityVerifier.mf
deleted file mode 100644
index 6118b31..0000000
--- a/verity/VerityVerifier.mf
+++ /dev/null
@@ -1 +0,0 @@
-Main-Class: com.android.verity.VerityVerifier
diff --git a/verity/verity_verifier b/verity/verity_verifier
deleted file mode 100755
index f145228..0000000
--- a/verity/verity_verifier
+++ /dev/null
@@ -1,8 +0,0 @@
-#!/bin/sh
-
-# Start-up script for VerityVerifier
-
-VERITYVERIFIER_HOME=`dirname "$0"`
-VERITYVERIFIER_HOME=`dirname "$VERITYVERIFIER_HOME"`
-
-java -Xmx512M -jar "$VERITYVERIFIER_HOME"/framework/VerityVerifier.jar "$@"
diff --git a/verity/verity_verifier.cpp b/verity/verity_verifier.cpp
new file mode 100644
index 0000000..bb4b6c2
--- /dev/null
+++ b/verity/verity_verifier.cpp
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <memory>
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/test_utils.h>
+#include <android-base/unique_fd.h>
+#include <crypto_utils/android_pubkey.h>
+#include <fec/io.h>
+#include <openssl/obj_mac.h>
+#include <openssl/rsa.h>
+#include <openssl/sha.h>
+#include <sparse/sparse.h>
+
+static RSA* load_key(const char* path) {
+ std::string content;
+ if (!android::base::ReadFileToString(path, &content) ||
+ content.size() < ANDROID_PUBKEY_ENCODED_SIZE) {
+ fprintf(stderr, "Failed to load key from %s\n", path);
+ return nullptr;
+ }
+
+ RSA* key = nullptr;
+ if (!android_pubkey_decode(reinterpret_cast<const uint8_t*>(content.c_str()),
+ ANDROID_PUBKEY_ENCODED_SIZE, &key)) {
+ fprintf(stderr, "Failed to parse key!\n");
+ return nullptr;
+ }
+
+ return key;
+}
+
+static int verify_table(const char* key_path, const uint8_t* signature, size_t signature_size,
+ const char* table, uint32_t table_length) {
+ // Hash the table
+ uint8_t hash_buf[SHA256_DIGEST_LENGTH];
+ SHA256(const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(table)), table_length, hash_buf);
+
+ // Now get the public key from the keyfile
+ std::unique_ptr<RSA, decltype(&RSA_free)> key(load_key(key_path), RSA_free);
+ if (!key) {
+ fprintf(stderr, "Couldn't load verity keys\n");
+ return -1;
+ }
+
+ // Verify the result
+ if (!RSA_verify(NID_sha256, hash_buf, sizeof(hash_buf), signature, signature_size, key.get())) {
+ fprintf(stderr, "Couldn't verify table\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+int main(int argc, char* argv[]) {
+ if (argc != 4 || strcmp(argv[2], "-mincrypt") != 0) {
+ printf("Usage: %s <image> -mincrypt <verity_key>\n"
+ " image the image file (raw or sparse image) to be verified\n"
+ " verity_key the verity key in mincrypt format (/verity_key on device)\n", argv[0]);
+ return 2;
+ }
+
+ // Get the raw image.
+ android::base::unique_fd fd(open(argv[1], O_RDONLY));
+ if (!fd) {
+ fprintf(stderr, "failed to open %s: %s\n", argv[1], strerror(errno));
+ return 1;
+ }
+
+ struct sparse_file* file = sparse_file_import_auto(fd, false, false);
+ if (file == nullptr) {
+ fprintf(stderr, "failed to read file %s\n", argv[1]);
+ return 1;
+ }
+
+ TemporaryFile tf;
+ if (sparse_file_write(file, tf.fd, false, false, false) < 0) {
+ fprintf(stderr, "failed to write output file\n");
+ return 1;
+ }
+ sparse_file_destroy(file);
+
+ // Verify.
+ fec::io input(tf.path);
+ if (!input) {
+ return 1;
+ }
+
+ fec_verity_metadata verity;
+ if (!input.get_verity_metadata(verity)) {
+ fprintf(stderr, "failed to get verity metadata\n");
+ return 1;
+ }
+
+ int ret = verify_table(argv[3], verity.signature, sizeof(verity.signature),
+ verity.table, verity.table_length);
+ printf("%s\n", ret == 0 ? "VERIFIED" : "FAILED");
+ return ret;
+}