Merge "Change /configfs/sdcardfs to 0770"
diff --git a/adb/commandline.cpp b/adb/commandline.cpp
index d126f52..6a80bcd 100644
--- a/adb/commandline.cpp
+++ b/adb/commandline.cpp
@@ -1944,7 +1944,8 @@
for (int i = argc - 1; i >= 0; i--) {
const char* file = argv[i];
- if (android::base::EndsWithIgnoreCase(file, ".apk")) {
+ if (android::base::EndsWithIgnoreCase(file, ".apk") ||
+ android::base::EndsWithIgnoreCase(file, ".dm")) {
struct stat sb;
if (stat(file, &sb) != -1) total_size += sb.st_size;
first_apk = i;
@@ -2005,9 +2006,8 @@
}
std::string cmd = android::base::StringPrintf(
- "%s install-write -S %" PRIu64 " %d %d_%s -",
- install_cmd.c_str(), static_cast<uint64_t>(sb.st_size), session_id, i,
- android::base::Basename(file).c_str());
+ "%s install-write -S %" PRIu64 " %d %s -", install_cmd.c_str(),
+ static_cast<uint64_t>(sb.st_size), session_id, android::base::Basename(file).c_str());
int localFd = adb_open(file, O_RDONLY);
if (localFd < 0) {
diff --git a/base/include/android-base/thread_annotations.h b/base/include/android-base/thread_annotations.h
index fbb5923..1307f0e 100644
--- a/base/include/android-base/thread_annotations.h
+++ b/base/include/android-base/thread_annotations.h
@@ -38,6 +38,12 @@
#define PT_GUARDED_BY(x) \
THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded_by(x))
+#define EXCLUSIVE_LOCKS_REQUIRED(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(exclusive_locks_required(__VA_ARGS__))
+
+#define SHARED_LOCKS_REQUIRED(...) \
+ THREAD_ANNOTATION_ATTRIBUTE__(shared_locks_required(__VA_ARGS__))
+
#define ACQUIRED_BEFORE(...) \
THREAD_ANNOTATION_ATTRIBUTE__(acquired_before(__VA_ARGS__))
diff --git a/healthd/Android.bp b/healthd/Android.bp
index 3a71d03..df12ff0 100644
--- a/healthd/Android.bp
+++ b/healthd/Android.bp
@@ -39,6 +39,7 @@
static_libs: [
"android.hardware.health@2.0-impl",
"android.hardware.health@1.0-convert",
+ "libhealthstoragedefault",
"libbatterymonitor",
],
@@ -72,6 +73,7 @@
"android.hardware.health@2.0-impl",
"android.hardware.health@1.0-convert",
"libbatterymonitor",
+ "libhealthstoragedefault",
],
shared_libs: [
diff --git a/healthd/Android.mk b/healthd/Android.mk
index 7e58785..7792eaf 100644
--- a/healthd/Android.mk
+++ b/healthd/Android.mk
@@ -50,6 +50,8 @@
android.hardware.health@2.0 \
android.hardware.health@2.0-impl \
android.hardware.health@1.0 \
+ android.hardware.health@1.0-convert \
+ libhealthstoragedefault \
libminui \
libpng \
libz \
@@ -95,9 +97,11 @@
android.hardware.health@2.0-impl \
android.hardware.health@2.0 \
android.hardware.health@1.0 \
+ android.hardware.health@1.0-convert \
libhidltransport \
libhidlbase \
libhwbinder \
+ libhealthstoragedefault \
libvndksupport \
libhealthd_charger \
libhealthd_draw \
diff --git a/healthd/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp
index 676ee41..561948c 100644
--- a/healthd/BatteryMonitor.cpp
+++ b/healthd/BatteryMonitor.cpp
@@ -86,6 +86,10 @@
initBatteryProperties(&props);
}
+struct BatteryProperties getBatteryProperties(BatteryMonitor* batteryMonitor) {
+ return batteryMonitor->props;
+}
+
int BatteryMonitor::getBatteryStatus(const char* status) {
int ret;
struct sysfsStringEnumMap batteryStatusMap[] = {
diff --git a/healthd/HealthServiceCommon.cpp b/healthd/HealthServiceCommon.cpp
index e8fbcb0..a2ca44b 100644
--- a/healthd/HealthServiceCommon.cpp
+++ b/healthd/HealthServiceCommon.cpp
@@ -30,7 +30,7 @@
using android::hardware::configureRpcThreadpool;
using android::hardware::handleTransportPoll;
using android::hardware::setupTransportPolling;
-using android::hardware::health::V1_0::HealthInfo;
+using android::hardware::health::V2_0::HealthInfo;
using android::hardware::health::V1_0::hal_conversion::convertToHealthInfo;
using android::hardware::health::V2_0::IHealth;
using android::hardware::health::V2_0::implementation::Health;
@@ -71,8 +71,8 @@
void healthd_mode_service_2_0_battery_update(struct android::BatteryProperties* prop) {
HealthInfo info;
- convertToHealthInfo(prop, info);
- Health::getImplementation()->notifyListeners(info);
+ convertToHealthInfo(prop, info.legacy);
+ Health::getImplementation()->notifyListeners(&info);
}
static struct healthd_mode_ops healthd_mode_service_2_0_ops = {
diff --git a/healthd/include/healthd/BatteryMonitor.h b/healthd/include/healthd/BatteryMonitor.h
index f9067cc..b6a1b66 100644
--- a/healthd/include/healthd/BatteryMonitor.h
+++ b/healthd/include/healthd/BatteryMonitor.h
@@ -42,6 +42,7 @@
int getChargeStatus();
status_t getProperty(int id, struct BatteryProperty *val);
void dumpState(int fd);
+ friend struct BatteryProperties getBatteryProperties(BatteryMonitor* batteryMonitor);
private:
struct healthd_config *mHealthdConfig;
diff --git a/init/Android.bp b/init/Android.bp
index 2fea359..0db7824 100644
--- a/init/Android.bp
+++ b/init/Android.bp
@@ -75,6 +75,7 @@
"persistent_properties.cpp",
"persistent_properties.proto",
"property_service.cpp",
+ "property_type.cpp",
"security.cpp",
"selinux.cpp",
"service.cpp",
@@ -178,6 +179,7 @@
"init_test.cpp",
"persistent_properties_test.cpp",
"property_service_test.cpp",
+ "property_type_test.cpp",
"result_test.cpp",
"rlimit_parser_test.cpp",
"service_test.cpp",
diff --git a/init/action.cpp b/init/action.cpp
index ab51eea..ba03e66 100644
--- a/init/action.cpp
+++ b/init/action.cpp
@@ -21,6 +21,7 @@
#include <android-base/properties.h>
#include <android-base/strings.h>
+#include "stable_properties.h"
#include "util.h"
using android::base::Join;
@@ -50,12 +51,19 @@
: func_(std::move(f)), execute_in_subcontext_(execute_in_subcontext), args_(args), line_(line) {}
Result<Success> Command::InvokeFunc(Subcontext* subcontext) const {
- if (execute_in_subcontext_ && subcontext) {
- return subcontext->Execute(args_);
- } else {
- const std::string& context = subcontext ? subcontext->context() : kInitContext;
- return RunBuiltinFunction(func_, args_, context);
+ if (subcontext) {
+ if (execute_in_subcontext_) {
+ return subcontext->Execute(args_);
+ }
+
+ auto expanded_args = subcontext->ExpandArgs(args_);
+ if (!expanded_args) {
+ return expanded_args.error();
+ }
+ return RunBuiltinFunction(func_, *expanded_args, subcontext->context());
}
+
+ return RunBuiltinFunction(func_, args_, kInitContext);
}
std::string Command::BuildCommandString() const {
@@ -127,6 +135,25 @@
}
}
+static bool IsActionableProperty(Subcontext* subcontext, const std::string& prop_name) {
+ static bool enabled =
+ android::base::GetBoolProperty("ro.actionable_compatible_property.enabled", false);
+
+ if (subcontext == nullptr || !enabled) {
+ return true;
+ }
+
+ if (kExportedActionableProperties.count(prop_name) == 1) {
+ return true;
+ }
+ for (const auto& prefix : kPartnerPrefixes) {
+ if (android::base::StartsWith(prop_name, prefix)) {
+ return true;
+ }
+ }
+ return false;
+}
+
Result<Success> Action::ParsePropertyTrigger(const std::string& trigger) {
const static std::string prop_str("property:");
std::string prop_name(trigger.substr(prop_str.length()));
@@ -138,6 +165,10 @@
std::string prop_value(prop_name.substr(equal_pos + 1));
prop_name.erase(equal_pos);
+ if (!IsActionableProperty(subcontext_, prop_name)) {
+ return Error() << "unexported property tigger found: " << prop_name;
+ }
+
if (auto [it, inserted] = property_triggers_.emplace(prop_name, prop_value); !inserted) {
return Error() << "multiple property triggers found for same property";
}
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 7aa94b0..79f7f25 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -58,6 +58,7 @@
#include "init.h"
#include "persistent_properties.h"
+#include "property_type.h"
#include "util.h"
using android::base::ReadFileToString;
@@ -95,14 +96,9 @@
LOG(FATAL) << "Failed to load serialized property info file";
}
}
-static bool check_mac_perms(const std::string& name, char* sctx, struct ucred* cr) {
- if (!sctx) {
- return false;
- }
-
- const char* target_context = nullptr;
- property_info_area->GetPropertyInfo(name.c_str(), &target_context, nullptr);
- if (target_context == nullptr) {
+static bool CheckMacPerms(const std::string& name, const char* target_context,
+ const char* source_context, struct ucred* cr) {
+ if (!target_context || !source_context) {
return false;
}
@@ -111,29 +107,12 @@
audit_data.name = name.c_str();
audit_data.cr = cr;
- bool has_access =
- (selinux_check_access(sctx, target_context, "property_service", "set", &audit_data) == 0);
+ bool has_access = (selinux_check_access(source_context, target_context, "property_service",
+ "set", &audit_data) == 0);
return has_access;
}
-static int check_control_mac_perms(const char *name, char *sctx, struct ucred *cr)
-{
- /*
- * Create a name prefix out of ctl.<service name>
- * The new prefix allows the use of the existing
- * property service backend labeling while avoiding
- * mislabels based on true property prefixes.
- */
- char ctl_name[PROP_VALUE_MAX+4];
- int ret = snprintf(ctl_name, sizeof(ctl_name), "ctl.%s", name);
-
- if (ret < 0 || (size_t) ret >= sizeof(ctl_name))
- return 0;
-
- return check_mac_perms(ctl_name, sctx, cr);
-}
-
bool is_legal_property_name(const std::string& name) {
size_t namelen = name.size();
@@ -422,52 +401,70 @@
struct ucred cr = socket.cred();
char* source_ctx = nullptr;
getpeercon(socket.socket(), &source_ctx);
+ std::string source_context = source_ctx;
+ freecon(source_ctx);
if (StartsWith(name, "ctl.")) {
- if (check_control_mac_perms(value.c_str(), source_ctx, &cr)) {
+ // ctl. properties have their name ctl.<action> and their value is the name of the service to
+ // apply that action to. Permissions for these actions are based on the service, so we must
+ // create a fake name of ctl.<service> to check permissions.
+ auto control_string = "ctl." + value;
+ const char* target_context = nullptr;
+ const char* type = nullptr;
+ property_info_area->GetPropertyInfo(control_string.c_str(), &target_context, &type);
+ if (!CheckMacPerms(control_string, target_context, source_context.c_str(), &cr)) {
+ LOG(ERROR) << "sys_prop(" << cmd_name << "): Unable to " << (name.c_str() + 4)
+ << " service ctl [" << value << "]"
+ << " uid:" << cr.uid << " gid:" << cr.gid << " pid:" << cr.pid;
+ if (!legacy_protocol) {
+ socket.SendUint32(PROP_ERROR_HANDLE_CONTROL_MESSAGE);
+ }
+ return;
+ }
+
handle_control_message(name.c_str() + 4, value.c_str());
if (!legacy_protocol) {
- socket.SendUint32(PROP_SUCCESS);
+ socket.SendUint32(PROP_SUCCESS);
}
- } else {
- LOG(ERROR) << "sys_prop(" << cmd_name << "): Unable to " << (name.c_str() + 4)
- << " service ctl [" << value << "]"
- << " uid:" << cr.uid
- << " gid:" << cr.gid
- << " pid:" << cr.pid;
- if (!legacy_protocol) {
- socket.SendUint32(PROP_ERROR_HANDLE_CONTROL_MESSAGE);
- }
- }
} else {
- if (check_mac_perms(name, source_ctx, &cr)) {
+ const char* target_context = nullptr;
+ const char* type = nullptr;
+ property_info_area->GetPropertyInfo(name.c_str(), &target_context, &type);
+ if (!CheckMacPerms(name, target_context, source_context.c_str(), &cr)) {
+ LOG(ERROR) << "sys_prop(" << cmd_name << "): permission denied uid:" << cr.uid
+ << " name:" << name;
+ if (!legacy_protocol) {
+ socket.SendUint32(PROP_ERROR_PERMISSION_DENIED);
+ }
+ return;
+ }
+ if (type == nullptr || !CheckType(type, value)) {
+ LOG(ERROR) << "sys_prop(" << cmd_name << "): type check failed, type: '"
+ << (type ?: "(null)") << "' value: '" << value << "'";
+ if (!legacy_protocol) {
+ socket.SendUint32(PROP_ERROR_INVALID_VALUE);
+ }
+ return;
+ }
// sys.powerctl is a special property that is used to make the device reboot. We want to log
// any process that sets this property to be able to accurately blame the cause of a shutdown.
if (name == "sys.powerctl") {
- std::string cmdline_path = StringPrintf("proc/%d/cmdline", cr.pid);
- std::string process_cmdline;
- std::string process_log_string;
- if (ReadFileToString(cmdline_path, &process_cmdline)) {
- // Since cmdline is null deliminated, .c_str() conveniently gives us just the process path.
- process_log_string = StringPrintf(" (%s)", process_cmdline.c_str());
- }
- LOG(INFO) << "Received sys.powerctl='" << value << "' from pid: " << cr.pid
- << process_log_string;
+ std::string cmdline_path = StringPrintf("proc/%d/cmdline", cr.pid);
+ std::string process_cmdline;
+ std::string process_log_string;
+ if (ReadFileToString(cmdline_path, &process_cmdline)) {
+ // Since cmdline is null deliminated, .c_str() conveniently gives us just the process path.
+ process_log_string = StringPrintf(" (%s)", process_cmdline.c_str());
+ }
+ LOG(INFO) << "Received sys.powerctl='" << value << "' from pid: " << cr.pid
+ << process_log_string;
}
uint32_t result = property_set(name, value);
if (!legacy_protocol) {
- socket.SendUint32(result);
+ socket.SendUint32(result);
}
- } else {
- LOG(ERROR) << "sys_prop(" << cmd_name << "): permission denied uid:" << cr.uid << " name:" << name;
- if (!legacy_protocol) {
- socket.SendUint32(PROP_ERROR_PERMISSION_DENIED);
- }
- }
}
-
- freecon(source_ctx);
}
static void handle_property_set_fd() {
@@ -764,9 +761,10 @@
}
LoadPropertyInfoFromFile("/nonplat_property_contexts", &property_infos);
}
+
auto serialized_contexts = std::string();
auto error = std::string();
- if (!BuildTrie(property_infos, "u:object_r:default_prop:s0", "\\s*", &serialized_contexts,
+ if (!BuildTrie(property_infos, "u:object_r:default_prop:s0", "string", &serialized_contexts,
&error)) {
LOG(ERROR) << "Unable to serialize property contexts: " << error;
return;
diff --git a/init/property_type.cpp b/init/property_type.cpp
new file mode 100644
index 0000000..249b12b
--- /dev/null
+++ b/init/property_type.cpp
@@ -0,0 +1,81 @@
+//
+// Copyright (C) 2017 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 "property_type.h"
+
+#include <android-base/parsedouble.h>
+#include <android-base/parseint.h>
+#include <android-base/strings.h>
+
+using android::base::ParseDouble;
+using android::base::ParseInt;
+using android::base::ParseUint;
+using android::base::Split;
+
+namespace android {
+namespace init {
+
+bool CheckType(const std::string& type_string, const std::string& value) {
+ auto type_strings = Split(type_string, " ");
+ if (type_strings.empty()) {
+ return false;
+ }
+ auto type = type_strings[0];
+
+ if (type == "string") {
+ return true;
+ }
+ if (type == "bool") {
+ return value == "true" || value == "false" || value == "1" || value == "0";
+ }
+ if (type == "int") {
+ int64_t parsed;
+ return ParseInt(value, &parsed);
+ }
+ if (type == "uint") {
+ uint64_t parsed;
+ if (value.empty() || value.front() == '-') {
+ return false;
+ }
+ return ParseUint(value, &parsed);
+ }
+ if (type == "double") {
+ double parsed;
+ return ParseDouble(value.c_str(), &parsed);
+ }
+ if (type == "size") {
+ auto it = value.begin();
+ while (it != value.end() && isdigit(*it)) {
+ it++;
+ }
+ if (it == value.begin() || it == value.end() || (*it != 'g' && *it != 'k' && *it != 'm')) {
+ return false;
+ }
+ it++;
+ return it == value.end();
+ }
+ if (type == "enum") {
+ for (auto it = std::next(type_strings.begin()); it != type_strings.end(); ++it) {
+ if (*it == value) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+} // namespace init
+} // namespace android
diff --git a/init/property_type.h b/init/property_type.h
new file mode 100644
index 0000000..c889e16
--- /dev/null
+++ b/init/property_type.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2017 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 _INIT_PROPERTY_TYPE_H
+#define _INIT_PROPERTY_TYPE_H
+
+#include <string>
+
+namespace android {
+namespace init {
+
+bool CheckType(const std::string& type_string, const std::string& value);
+
+} // namespace init
+} // namespace android
+
+#endif
diff --git a/init/property_type_test.cpp b/init/property_type_test.cpp
new file mode 100644
index 0000000..068bccc
--- /dev/null
+++ b/init/property_type_test.cpp
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2017 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 "property_type.h"
+
+#include <gtest/gtest.h>
+
+namespace android {
+namespace init {
+
+TEST(property_type, CheckType_string) {
+ EXPECT_TRUE(CheckType("string", ""));
+ EXPECT_TRUE(CheckType("string", "-234"));
+ EXPECT_TRUE(CheckType("string", "234"));
+ EXPECT_TRUE(CheckType("string", "true"));
+ EXPECT_TRUE(CheckType("string", "false"));
+ EXPECT_TRUE(CheckType("string", "45645634563456345634563456"));
+ EXPECT_TRUE(CheckType("string", "some other string"));
+}
+
+TEST(property_type, CheckType_int) {
+ EXPECT_FALSE(CheckType("int", ""));
+ EXPECT_FALSE(CheckType("int", "abc"));
+ EXPECT_FALSE(CheckType("int", "-abc"));
+ EXPECT_TRUE(CheckType("int", "0"));
+ EXPECT_TRUE(CheckType("int", std::to_string(std::numeric_limits<int64_t>::min())));
+ EXPECT_TRUE(CheckType("int", std::to_string(std::numeric_limits<int64_t>::max())));
+ EXPECT_TRUE(CheckType("int", "123"));
+ EXPECT_TRUE(CheckType("int", "-123"));
+}
+
+TEST(property_type, CheckType_uint) {
+ EXPECT_FALSE(CheckType("uint", ""));
+ EXPECT_FALSE(CheckType("uint", "abc"));
+ EXPECT_FALSE(CheckType("uint", "-abc"));
+ EXPECT_TRUE(CheckType("uint", "0"));
+ EXPECT_TRUE(CheckType("uint", std::to_string(std::numeric_limits<uint64_t>::max())));
+ EXPECT_TRUE(CheckType("uint", "123"));
+ EXPECT_FALSE(CheckType("uint", "-123"));
+}
+
+TEST(property_type, CheckType_double) {
+ EXPECT_FALSE(CheckType("double", ""));
+ EXPECT_FALSE(CheckType("double", "abc"));
+ EXPECT_FALSE(CheckType("double", "-abc"));
+ EXPECT_TRUE(CheckType("double", "0.0"));
+ EXPECT_TRUE(CheckType("double", std::to_string(std::numeric_limits<double>::min())));
+ EXPECT_TRUE(CheckType("double", std::to_string(std::numeric_limits<double>::max())));
+ EXPECT_TRUE(CheckType("double", "123.1"));
+ EXPECT_TRUE(CheckType("double", "-123.1"));
+}
+
+TEST(property_type, CheckType_size) {
+ EXPECT_FALSE(CheckType("size", ""));
+ EXPECT_FALSE(CheckType("size", "ab"));
+ EXPECT_FALSE(CheckType("size", "abcd"));
+ EXPECT_FALSE(CheckType("size", "0"));
+
+ EXPECT_TRUE(CheckType("size", "512g"));
+ EXPECT_TRUE(CheckType("size", "512k"));
+ EXPECT_TRUE(CheckType("size", "512m"));
+
+ EXPECT_FALSE(CheckType("size", "512gggg"));
+ EXPECT_FALSE(CheckType("size", "512mgk"));
+ EXPECT_FALSE(CheckType("size", "g"));
+ EXPECT_FALSE(CheckType("size", "m"));
+}
+
+TEST(property_type, CheckType_enum) {
+ EXPECT_FALSE(CheckType("enum abc", ""));
+ EXPECT_FALSE(CheckType("enum abc", "ab"));
+ EXPECT_FALSE(CheckType("enum abc", "abcd"));
+ EXPECT_FALSE(CheckType("enum 123 456 789", "0"));
+
+ EXPECT_TRUE(CheckType("enum abc", "abc"));
+ EXPECT_TRUE(CheckType("enum 123 456 789", "123"));
+ EXPECT_TRUE(CheckType("enum 123 456 789", "456"));
+ EXPECT_TRUE(CheckType("enum 123 456 789", "789"));
+}
+
+} // namespace init
+} // namespace android
diff --git a/init/stable_properties.h b/init/stable_properties.h
new file mode 100644
index 0000000..8219838
--- /dev/null
+++ b/init/stable_properties.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2018 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 _INIT_STABLE_PROPERTIES_H
+#define _INIT_STABLE_PROPERTIES_H
+
+#include <set>
+#include <string>
+
+namespace android {
+namespace init {
+
+static constexpr const char* kPartnerPrefixes[] = {
+ "init.svc.vendor.", "ro.vendor.", "persist.vendor.", "vendor.",
+ "init.svc.odm.", "ro.odm.", "persist.odm.", "odm.",
+};
+
+static const std::set<std::string> kExportedActionableProperties = {
+ "init.svc.zygote", "persist.bluetooth.btsnoopenable",
+ "persist.sys.crash_rcu", "persist.sys.zram_enabled",
+ "ro.boot.revision", "ro.bootmode",
+ "ro.build.type", "sys.boot_completed",
+ "sys.retaildemo.enabled", "sys.shutdown.requested",
+ "sys.usb.config", "sys.usb.configfs",
+ "sys.usb.ffs.mtp.ready", "sys.usb.ffs.ready",
+ "sys.user.0.ce_available", "sys.vdso",
+ "vts.native_server.on",
+};
+
+} // namespace init
+} // namespace android
+
+#endif
diff --git a/init/subcontext.cpp b/init/subcontext.cpp
index 068be6e..be754da 100644
--- a/init/subcontext.cpp
+++ b/init/subcontext.cpp
@@ -28,7 +28,6 @@
#include "action.h"
#include "selinux.h"
-#include "system/core/init/subcontext.pb.h"
#include "util.h"
using android::base::GetExecutablePath;
@@ -84,7 +83,9 @@
private:
void RunCommand(const SubcontextCommand::ExecuteCommand& execute_command,
- SubcontextReply::ResultMessage* result_message) const;
+ SubcontextReply* reply) const;
+ void ExpandArgs(const SubcontextCommand::ExpandArgsCommand& expand_args_command,
+ SubcontextReply* reply) const;
const KeywordFunctionMap* function_map_;
const std::string context_;
@@ -92,7 +93,7 @@
};
void SubcontextProcess::RunCommand(const SubcontextCommand::ExecuteCommand& execute_command,
- SubcontextReply::ResultMessage* result_message) const {
+ SubcontextReply* reply) const {
// Need to use ArraySplice instead of this code.
auto args = std::vector<std::string>();
for (const auto& string : execute_command.args()) {
@@ -108,11 +109,27 @@
}
if (result) {
- result_message->set_success(true);
+ reply->set_success(true);
} else {
- result_message->set_success(false);
- result_message->set_error_string(result.error_string());
- result_message->set_error_errno(result.error_errno());
+ auto* failure = reply->mutable_failure();
+ failure->set_error_string(result.error_string());
+ failure->set_error_errno(result.error_errno());
+ }
+}
+
+void SubcontextProcess::ExpandArgs(const SubcontextCommand::ExpandArgsCommand& expand_args_command,
+ SubcontextReply* reply) const {
+ for (const auto& arg : expand_args_command.args()) {
+ auto expanded_prop = std::string{};
+ if (!expand_props(arg, &expanded_prop)) {
+ auto* failure = reply->mutable_failure();
+ failure->set_error_string("Failed to expand '" + arg + "'");
+ failure->set_error_errno(0);
+ return;
+ } else {
+ auto* expand_args_reply = reply->mutable_expand_args_reply();
+ expand_args_reply->add_expanded_args(expanded_prop);
+ }
}
}
@@ -142,7 +159,11 @@
auto reply = SubcontextReply();
switch (subcontext_command.command_case()) {
case SubcontextCommand::kExecuteCommand: {
- RunCommand(subcontext_command.execute_command(), reply.mutable_result());
+ RunCommand(subcontext_command.execute_command(), &reply);
+ break;
+ }
+ case SubcontextCommand::kExpandArgsCommand: {
+ ExpandArgs(subcontext_command.expand_args_command(), &reply);
break;
}
default:
@@ -219,12 +240,7 @@
Fork();
}
-Result<Success> Subcontext::Execute(const std::vector<std::string>& args) {
- auto subcontext_command = SubcontextCommand();
- std::copy(
- args.begin(), args.end(),
- RepeatedPtrFieldBackInserter(subcontext_command.mutable_execute_command()->mutable_args()));
-
+Result<SubcontextReply> Subcontext::TransmitMessage(const SubcontextCommand& subcontext_command) {
if (auto result = SendMessage(socket_, subcontext_command); !result) {
Restart();
return ErrnoError() << "Failed to send message to subcontext";
@@ -236,25 +252,59 @@
return Error() << "Failed to receive result from subcontext: " << subcontext_message.error();
}
- auto subcontext_reply = SubcontextReply();
+ auto subcontext_reply = SubcontextReply{};
if (!subcontext_reply.ParseFromString(*subcontext_message)) {
Restart();
return Error() << "Unable to parse message from subcontext";
}
-
- switch (subcontext_reply.reply_case()) {
- case SubcontextReply::kResult: {
- auto result = subcontext_reply.result();
- if (result.success()) {
- return Success();
- } else {
- return ResultError(result.error_string(), result.error_errno());
- }
- }
- default:
- return Error() << "Unknown message type from subcontext: "
- << subcontext_reply.reply_case();
+ if (subcontext_reply.reply_case() == SubcontextReply::kFailure) {
+ auto& failure = subcontext_reply.failure();
+ return ResultError(failure.error_string(), failure.error_errno());
}
+ return subcontext_reply;
+}
+
+Result<Success> Subcontext::Execute(const std::vector<std::string>& args) {
+ auto subcontext_command = SubcontextCommand();
+ std::copy(
+ args.begin(), args.end(),
+ RepeatedPtrFieldBackInserter(subcontext_command.mutable_execute_command()->mutable_args()));
+
+ auto subcontext_reply = TransmitMessage(subcontext_command);
+ if (!subcontext_reply) {
+ return subcontext_reply.error();
+ }
+
+ if (subcontext_reply->reply_case() != SubcontextReply::kSuccess) {
+ return Error() << "Unexpected message type from subcontext: "
+ << subcontext_reply->reply_case();
+ }
+
+ return Success();
+}
+
+Result<std::vector<std::string>> Subcontext::ExpandArgs(const std::vector<std::string>& args) {
+ auto subcontext_command = SubcontextCommand{};
+ std::copy(args.begin(), args.end(),
+ RepeatedPtrFieldBackInserter(
+ subcontext_command.mutable_expand_args_command()->mutable_args()));
+
+ auto subcontext_reply = TransmitMessage(subcontext_command);
+ if (!subcontext_reply) {
+ return subcontext_reply.error();
+ }
+
+ if (subcontext_reply->reply_case() != SubcontextReply::kExpandArgsReply) {
+ return Error() << "Unexpected message type from subcontext: "
+ << subcontext_reply->reply_case();
+ }
+
+ auto& reply = subcontext_reply->expand_args_reply();
+ auto expanded_args = std::vector<std::string>{};
+ for (const auto& string : reply.expanded_args()) {
+ expanded_args.emplace_back(string);
+ }
+ return expanded_args;
}
static std::vector<Subcontext> subcontexts;
diff --git a/init/subcontext.h b/init/subcontext.h
index eadabee..262440d 100644
--- a/init/subcontext.h
+++ b/init/subcontext.h
@@ -25,6 +25,7 @@
#include <android-base/unique_fd.h>
#include "builtins.h"
+#include "system/core/init/subcontext.pb.h"
namespace android {
namespace init {
@@ -39,7 +40,8 @@
Fork();
}
- Result<Success> Execute(const std::vector<std::string>& command);
+ Result<Success> Execute(const std::vector<std::string>& args);
+ Result<std::vector<std::string>> ExpandArgs(const std::vector<std::string>& args);
void Restart();
const std::string& path_prefix() const { return path_prefix_; }
@@ -48,6 +50,7 @@
private:
void Fork();
+ Result<SubcontextReply> TransmitMessage(const SubcontextCommand& subcontext_command);
std::string path_prefix_;
std::string context_;
diff --git a/init/subcontext.proto b/init/subcontext.proto
index 0d89734..e68115e 100644
--- a/init/subcontext.proto
+++ b/init/subcontext.proto
@@ -19,15 +19,23 @@
message SubcontextCommand {
message ExecuteCommand { repeated string args = 1; }
- oneof command { ExecuteCommand execute_command = 1; }
+ message ExpandArgsCommand { repeated string args = 1; }
+ oneof command {
+ ExecuteCommand execute_command = 1;
+ ExpandArgsCommand expand_args_command = 2;
+ }
}
message SubcontextReply {
- message ResultMessage {
- optional bool success = 1;
- optional string error_string = 2;
- optional int32 error_errno = 3;
+ message Failure {
+ optional string error_string = 1;
+ optional int32 error_errno = 2;
}
+ message ExpandArgsReply { repeated string expanded_args = 1; }
- oneof reply { ResultMessage result = 1; }
+ oneof reply {
+ bool success = 1;
+ Failure failure = 2;
+ ExpandArgsReply expand_args_reply = 3;
+ }
}
\ No newline at end of file
diff --git a/init/subcontext_test.cpp b/init/subcontext_test.cpp
index ca45266..230203a 100644
--- a/init/subcontext_test.cpp
+++ b/init/subcontext_test.cpp
@@ -143,6 +143,34 @@
});
}
+TEST(subcontext, ExpandArgs) {
+ RunTest([](auto& subcontext, auto& context_string) {
+ auto args = std::vector<std::string>{
+ "first",
+ "${ro.hardware}",
+ "$$third",
+ };
+ auto result = subcontext.ExpandArgs(args);
+ ASSERT_TRUE(result) << result.error();
+ ASSERT_EQ(3U, result->size());
+ EXPECT_EQ(args[0], result->at(0));
+ EXPECT_EQ(GetProperty("ro.hardware", ""), result->at(1));
+ EXPECT_EQ("$third", result->at(2));
+ });
+}
+
+TEST(subcontext, ExpandArgsFailure) {
+ RunTest([](auto& subcontext, auto& context_string) {
+ auto args = std::vector<std::string>{
+ "first",
+ "${",
+ };
+ auto result = subcontext.ExpandArgs(args);
+ ASSERT_FALSE(result);
+ EXPECT_EQ("Failed to expand '" + args[1] + "'", result.error_string());
+ });
+}
+
TestFunctionMap BuildTestFunctionMap() {
TestFunctionMap test_function_map;
// For CheckDifferentPid
diff --git a/libbacktrace/UnwindStack.cpp b/libbacktrace/UnwindStack.cpp
index 2a555afc..b62ee16 100644
--- a/libbacktrace/UnwindStack.cpp
+++ b/libbacktrace/UnwindStack.cpp
@@ -50,6 +50,7 @@
auto process_memory = stack_map->process_memory();
unwindstack::Unwinder unwinder(MAX_BACKTRACE_FRAMES + num_ignore_frames, stack_map->stack_maps(),
regs, stack_map->process_memory());
+ unwinder.SetJitDebug(stack_map->GetJitDebug(), regs->Arch());
unwinder.Unwind(skip_names, &stack_map->GetSuffixesToIgnore());
if (num_ignore_frames >= unwinder.NumFrames()) {
@@ -69,6 +70,7 @@
back_frame->rel_pc = frame->rel_pc;
back_frame->pc = frame->pc;
back_frame->sp = frame->sp;
+ back_frame->dex_pc = frame->dex_pc;
back_frame->func_name = demangle(frame->function_name.c_str());
back_frame->func_offset = frame->function_offset;
diff --git a/libbacktrace/UnwindStackMap.cpp b/libbacktrace/UnwindStackMap.cpp
index 836a774..93406dc 100644
--- a/libbacktrace/UnwindStackMap.cpp
+++ b/libbacktrace/UnwindStackMap.cpp
@@ -18,6 +18,9 @@
#include <stdlib.h>
#include <sys/types.h>
+#include <string>
+#include <vector>
+
#include <backtrace/BacktraceMap.h>
#include <unwindstack/Elf.h>
#include <unwindstack/MapInfo.h>
@@ -39,6 +42,10 @@
// Create the process memory object.
process_memory_ = unwindstack::Memory::CreateProcessMemory(pid_);
+ // Create a JitDebug object for getting jit unwind information.
+ std::vector<std::string> search_libs_{"libart.so", "libartd.so"};
+ jit_debug_.reset(new unwindstack::JitDebug(process_memory_, search_libs_));
+
if (!stack_maps_->Parse()) {
return false;
}
diff --git a/libbacktrace/UnwindStackMap.h b/libbacktrace/UnwindStackMap.h
index 2f63655..12c5909 100644
--- a/libbacktrace/UnwindStackMap.h
+++ b/libbacktrace/UnwindStackMap.h
@@ -23,6 +23,7 @@
#include <memory>
#include <backtrace/BacktraceMap.h>
+#include <unwindstack/JitDebug.h>
#include <unwindstack/Maps.h>
class UnwindStackMap : public BacktraceMap {
@@ -41,11 +42,14 @@
const std::shared_ptr<unwindstack::Memory>& process_memory() { return process_memory_; }
+ unwindstack::JitDebug* GetJitDebug() { return jit_debug_.get(); }
+
protected:
uint64_t GetLoadBias(size_t index) override;
std::unique_ptr<unwindstack::Maps> stack_maps_;
std::shared_ptr<unwindstack::Memory> process_memory_;
+ std::unique_ptr<unwindstack::JitDebug> jit_debug_;
};
#endif // _LIBBACKTRACE_UNWINDSTACK_MAP_H
diff --git a/libbacktrace/include/backtrace/Backtrace.h b/libbacktrace/include/backtrace/Backtrace.h
index 5922664..f34fb2d 100644
--- a/libbacktrace/include/backtrace/Backtrace.h
+++ b/libbacktrace/include/backtrace/Backtrace.h
@@ -81,6 +81,7 @@
uintptr_t rel_pc; // The relative pc.
uintptr_t sp; // The top of the stack.
size_t stack_size; // The size of the stack, zero indicate an unknown stack size.
+ uint64_t dex_pc; // If non-zero, the Dex PC for the ART interpreter.
backtrace_map_t map; // The map associated with the given pc.
std::string func_name; // The function name associated with this pc, NULL if not found.
uintptr_t func_offset; // pc relative to the start of the function, only valid if func_name is not NULL.
diff --git a/libcutils/fs_config.cpp b/libcutils/fs_config.cpp
index f45472e..7c03663 100644
--- a/libcutils/fs_config.cpp
+++ b/libcutils/fs_config.cpp
@@ -185,11 +185,16 @@
{ 00755, AID_SYSTEM, AID_GRAPHICS, CAP_MASK_LONG(CAP_SYS_NICE),
"system/bin/surfaceflinger" },
- // Support hostapd administering a network interface.
+ // Support hostapd administering a network interface (Old Path: <= O-MR1 release).
{ 00755, AID_WIFI, AID_WIFI, CAP_MASK_LONG(CAP_NET_ADMIN) |
CAP_MASK_LONG(CAP_NET_RAW),
"vendor/bin/hostapd" },
+ // Support hostapd administering a network interface (New Path: >= P release).
+ { 00750, AID_WIFI, AID_WIFI, CAP_MASK_LONG(CAP_NET_ADMIN) |
+ CAP_MASK_LONG(CAP_NET_RAW),
+ "vendor/bin/hw/hostapd" },
+
// Support Bluetooth legacy hal accessing /sys/class/rfkill
// Support RT scheduling in Bluetooth
{ 00700, AID_BLUETOOTH, AID_BLUETOOTH, CAP_MASK_LONG(CAP_NET_ADMIN) |
diff --git a/liblog/Android.bp b/liblog/Android.bp
index eae0b10..7d9e306 100644
--- a/liblog/Android.bp
+++ b/liblog/Android.bp
@@ -80,6 +80,10 @@
address: false,
},
},
+ android_arm: {
+ // TODO: This is to work around b/24465209. Remove after root cause is fixed
+ ldflags: ["-Wl,--hash-style=both"],
+ },
windows: {
srcs: ["uio.c"],
enabled: true,
diff --git a/liblog/include/log/log_main.h b/liblog/include/log/log_main.h
index 339a06d..21fc7cc 100644
--- a/liblog/include/log/log_main.h
+++ b/liblog/include/log/log_main.h
@@ -17,6 +17,8 @@
#ifndef _LIBS_LOG_LOG_MAIN_H
#define _LIBS_LOG_LOG_MAIN_H
+#include <stdbool.h>
+
#include <android/log.h>
#include <sys/cdefs.h>
@@ -175,10 +177,10 @@
#if LOG_NDEBUG
#define ALOGV(...) \
do { \
- if (0) { \
+ if (false) { \
__ALOGV(__VA_ARGS__); \
} \
- } while (0)
+ } while (false)
#else
#define ALOGV(...) __ALOGV(__VA_ARGS__)
#endif
diff --git a/liblog/tests/Android.mk b/liblog/tests/Android.mk
index 39b52ac..cfa849b 100644
--- a/liblog/tests/Android.mk
+++ b/liblog/tests/Android.mk
@@ -24,13 +24,12 @@
test_tags := tests
benchmark_c_flags := \
- -Ibionic/tests \
- -Wall -Wextra \
+ -Wall \
+ -Wextra \
-Werror \
-fno-builtin \
benchmark_src_files := \
- benchmark_main.cpp \
liblog_benchmark.cpp
# Build benchmarks for the device. Run with:
@@ -41,7 +40,7 @@
LOCAL_CFLAGS += $(benchmark_c_flags)
LOCAL_SHARED_LIBRARIES += liblog libm libbase
LOCAL_SRC_FILES := $(benchmark_src_files)
-include $(BUILD_NATIVE_TEST)
+include $(BUILD_NATIVE_BENCHMARK)
# -----------------------------------------------------------------------------
# Unit tests.
diff --git a/liblog/tests/benchmark.h b/liblog/tests/benchmark.h
deleted file mode 100644
index e9280f6..0000000
--- a/liblog/tests/benchmark.h
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
- * Copyright (C) 2012-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.
- */
-
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include <vector>
-
-#ifndef BIONIC_BENCHMARK_H_
-#define BIONIC_BENCHMARK_H_
-
-namespace testing {
-
-class Benchmark;
-template <typename T> class BenchmarkWantsArg;
-template <typename T> class BenchmarkWithArg;
-
-void BenchmarkRegister(Benchmark* bm);
-int PrettyPrintInt(char* str, int len, unsigned int arg);
-
-class Benchmark {
- public:
- Benchmark(const char* name, void (*fn)(int)) : name_(strdup(name)), fn_(fn) {
- BenchmarkRegister(this);
- }
- explicit Benchmark(const char* name) : name_(strdup(name)), fn_(NULL) {}
-
- virtual ~Benchmark() {
- free(name_);
- }
-
- const char* Name() { return name_; }
- virtual const char* ArgName() { return NULL; }
- virtual void RunFn(int iterations) { fn_(iterations); }
-
- protected:
- char* name_;
-
- private:
- void (*fn_)(int);
-};
-
-template <typename T>
-class BenchmarkWantsArgBase : public Benchmark {
- public:
- BenchmarkWantsArgBase(const char* name, void (*fn)(int, T)) : Benchmark(name) {
- fn_arg_ = fn;
- }
-
- BenchmarkWantsArgBase<T>* Arg(const char* arg_name, T arg) {
- BenchmarkRegister(new BenchmarkWithArg<T>(name_, fn_arg_, arg_name, arg));
- return this;
- }
-
- protected:
- virtual void RunFn(int) { printf("can't run arg benchmark %s without arg\n", Name()); }
- void (*fn_arg_)(int, T);
-};
-
-template <typename T>
-class BenchmarkWithArg : public BenchmarkWantsArg<T> {
- public:
- BenchmarkWithArg(const char* name, void (*fn)(int, T), const char* arg_name, T arg) :
- BenchmarkWantsArg<T>(name, fn), arg_(arg) {
- arg_name_ = strdup(arg_name);
- }
-
- virtual ~BenchmarkWithArg() {
- free(arg_name_);
- }
-
- virtual const char* ArgName() { return arg_name_; }
-
- protected:
- virtual void RunFn(int iterations) { BenchmarkWantsArg<T>::fn_arg_(iterations, arg_); }
-
- private:
- T arg_;
- char* arg_name_;
-};
-
-template <typename T>
-class BenchmarkWantsArg : public BenchmarkWantsArgBase<T> {
- public:
- BenchmarkWantsArg<T>(const char* name, void (*fn)(int, T)) :
- BenchmarkWantsArgBase<T>(name, fn) { }
-};
-
-template <>
-class BenchmarkWantsArg<int> : public BenchmarkWantsArgBase<int> {
- public:
- BenchmarkWantsArg<int>(const char* name, void (*fn)(int, int)) :
- BenchmarkWantsArgBase<int>(name, fn) { }
-
- BenchmarkWantsArg<int>* Arg(int arg) {
- char arg_name[100];
- PrettyPrintInt(arg_name, sizeof(arg_name), arg);
- BenchmarkRegister(new BenchmarkWithArg<int>(name_, fn_arg_, arg_name, arg));
- return this;
- }
-};
-
-static inline Benchmark* BenchmarkFactory(const char* name, void (*fn)(int)) {
- return new Benchmark(name, fn);
-}
-
-template <typename T>
-static inline BenchmarkWantsArg<T>* BenchmarkFactory(const char* name, void (*fn)(int, T)) {
- return new BenchmarkWantsArg<T>(name, fn);
-}
-
-} // namespace testing
-
-template <typename T>
-static inline void BenchmarkAddArg(::testing::Benchmark* b, const char* name, T arg) {
- ::testing::BenchmarkWantsArg<T>* ba;
- ba = static_cast< ::testing::BenchmarkWantsArg<T>* >(b);
- ba->Arg(name, arg);
-}
-
-void SetBenchmarkBytesProcessed(uint64_t);
-void ResetBenchmarkTiming(void);
-void StopBenchmarkTiming(void);
-void StartBenchmarkTiming(void);
-void StartBenchmarkTiming(uint64_t);
-void StopBenchmarkTiming(uint64_t);
-
-#define BENCHMARK(f) \
- static ::testing::Benchmark* _benchmark_##f __attribute__((unused)) = /* NOLINT */ \
- (::testing::Benchmark*)::testing::BenchmarkFactory(#f, f) /* NOLINT */
-
-#endif // BIONIC_BENCHMARK_H_
diff --git a/liblog/tests/benchmark_main.cpp b/liblog/tests/benchmark_main.cpp
deleted file mode 100644
index 7367f1b..0000000
--- a/liblog/tests/benchmark_main.cpp
+++ /dev/null
@@ -1,247 +0,0 @@
-/*
- * Copyright (C) 2012-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.
- */
-
-#include <benchmark.h>
-
-#include <inttypes.h>
-#include <math.h>
-#include <regex.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-#include <map>
-#include <string>
-#include <vector>
-
-static uint64_t gBytesProcessed;
-static uint64_t gBenchmarkTotalTimeNs;
-static uint64_t gBenchmarkTotalTimeNsSquared;
-static uint64_t gBenchmarkNum;
-static uint64_t gBenchmarkStartTimeNs;
-
-typedef std::vector< ::testing::Benchmark*> BenchmarkList;
-static BenchmarkList* gBenchmarks;
-
-static int Round(int n) {
- int base = 1;
- while (base * 10 < n) {
- base *= 10;
- }
- if (n < 2 * base) {
- return 2 * base;
- }
- if (n < 5 * base) {
- return 5 * base;
- }
- return 10 * base;
-}
-
-static uint64_t NanoTime() {
- struct timespec t;
- t.tv_sec = t.tv_nsec = 0;
- clock_gettime(CLOCK_MONOTONIC, &t);
- return static_cast<uint64_t>(t.tv_sec) * 1000000000ULL + t.tv_nsec;
-}
-
-namespace testing {
-
-int PrettyPrintInt(char* str, int len, unsigned int arg) {
- if (arg >= (1 << 30) && arg % (1 << 30) == 0) {
- return snprintf(str, len, "%uGi", arg / (1 << 30));
- } else if (arg >= (1 << 20) && arg % (1 << 20) == 0) {
- return snprintf(str, len, "%uMi", arg / (1 << 20));
- } else if (arg >= (1 << 10) && arg % (1 << 10) == 0) {
- return snprintf(str, len, "%uKi", arg / (1 << 10));
- } else if (arg >= 1000000000 && arg % 1000000000 == 0) {
- return snprintf(str, len, "%uG", arg / 1000000000);
- } else if (arg >= 1000000 && arg % 1000000 == 0) {
- return snprintf(str, len, "%uM", arg / 1000000);
- } else if (arg >= 1000 && arg % 1000 == 0) {
- return snprintf(str, len, "%uK", arg / 1000);
- } else {
- return snprintf(str, len, "%u", arg);
- }
-}
-
-bool ShouldRun(Benchmark* b, int argc, char* argv[]) {
- if (argc == 1) {
- return true; // With no arguments, we run all benchmarks.
- }
- // Otherwise, we interpret each argument as a regular expression and
- // see if any of our benchmarks match.
- for (int i = 1; i < argc; i++) {
- regex_t re;
- if (regcomp(&re, argv[i], 0) != 0) {
- fprintf(stderr, "couldn't compile \"%s\" as a regular expression!\n",
- argv[i]);
- exit(EXIT_FAILURE);
- }
- int match = regexec(&re, b->Name(), 0, NULL, 0);
- regfree(&re);
- if (match != REG_NOMATCH) {
- return true;
- }
- }
- return false;
-}
-
-void BenchmarkRegister(Benchmark* b) {
- if (gBenchmarks == NULL) {
- gBenchmarks = new BenchmarkList;
- }
- gBenchmarks->push_back(b);
-}
-
-void RunRepeatedly(Benchmark* b, int iterations) {
- gBytesProcessed = 0;
- ResetBenchmarkTiming();
- uint64_t StartTimeNs = NanoTime();
- b->RunFn(iterations);
- // Catch us if we fail to log anything.
- if ((gBenchmarkTotalTimeNs == 0) && (StartTimeNs != 0) &&
- (gBenchmarkStartTimeNs == 0)) {
- gBenchmarkTotalTimeNs = NanoTime() - StartTimeNs;
- }
-}
-
-void Run(Benchmark* b) {
- // run once in case it's expensive
- unsigned iterations = 1;
- uint64_t s = NanoTime();
- RunRepeatedly(b, iterations);
- s = NanoTime() - s;
- while (s < 2e9 && gBenchmarkTotalTimeNs < 1e9 && iterations < 1e9) {
- unsigned last = iterations;
- if (gBenchmarkTotalTimeNs / iterations == 0) {
- iterations = 1e9;
- } else {
- iterations = 1e9 / (gBenchmarkTotalTimeNs / iterations);
- }
- iterations =
- std::max(last + 1, std::min(iterations + iterations / 2, 100 * last));
- iterations = Round(iterations);
- s = NanoTime();
- RunRepeatedly(b, iterations);
- s = NanoTime() - s;
- }
-
- char throughput[100];
- throughput[0] = '\0';
- if (gBenchmarkTotalTimeNs > 0 && gBytesProcessed > 0) {
- double mib_processed = static_cast<double>(gBytesProcessed) / 1e6;
- double seconds = static_cast<double>(gBenchmarkTotalTimeNs) / 1e9;
- snprintf(throughput, sizeof(throughput), " %8.2f MiB/s",
- mib_processed / seconds);
- }
-
- char full_name[100];
- snprintf(full_name, sizeof(full_name), "%s%s%s", b->Name(),
- b->ArgName() ? "/" : "", b->ArgName() ? b->ArgName() : "");
-
- uint64_t mean = gBenchmarkTotalTimeNs / iterations;
- uint64_t sdev = 0;
- if (gBenchmarkNum == iterations) {
- mean = gBenchmarkTotalTimeNs / gBenchmarkNum;
- uint64_t nXvariance = gBenchmarkTotalTimeNsSquared * gBenchmarkNum -
- (gBenchmarkTotalTimeNs * gBenchmarkTotalTimeNs);
- sdev = (sqrt((double)nXvariance) / gBenchmarkNum / gBenchmarkNum) + 0.5;
- }
- if (mean > (10000 * sdev)) {
- printf("%-25s %10" PRIu64 " %10" PRIu64 "%s\n", full_name,
- static_cast<uint64_t>(iterations), mean, throughput);
- } else {
- printf("%-25s %10" PRIu64 " %10" PRIu64 "(\317\203%" PRIu64 ")%s\n",
- full_name, static_cast<uint64_t>(iterations), mean, sdev, throughput);
- }
- fflush(stdout);
-}
-
-} // namespace testing
-
-void SetBenchmarkBytesProcessed(uint64_t x) {
- gBytesProcessed = x;
-}
-
-void ResetBenchmarkTiming() {
- gBenchmarkStartTimeNs = 0;
- gBenchmarkTotalTimeNs = 0;
- gBenchmarkTotalTimeNsSquared = 0;
- gBenchmarkNum = 0;
-}
-
-void StopBenchmarkTiming(void) {
- if (gBenchmarkStartTimeNs != 0) {
- int64_t diff = NanoTime() - gBenchmarkStartTimeNs;
- gBenchmarkTotalTimeNs += diff;
- gBenchmarkTotalTimeNsSquared += diff * diff;
- ++gBenchmarkNum;
- }
- gBenchmarkStartTimeNs = 0;
-}
-
-void StartBenchmarkTiming(void) {
- if (gBenchmarkStartTimeNs == 0) {
- gBenchmarkStartTimeNs = NanoTime();
- }
-}
-
-void StopBenchmarkTiming(uint64_t NanoTime) {
- if (gBenchmarkStartTimeNs != 0) {
- int64_t diff = NanoTime - gBenchmarkStartTimeNs;
- gBenchmarkTotalTimeNs += diff;
- gBenchmarkTotalTimeNsSquared += diff * diff;
- if (NanoTime != 0) {
- ++gBenchmarkNum;
- }
- }
- gBenchmarkStartTimeNs = 0;
-}
-
-void StartBenchmarkTiming(uint64_t NanoTime) {
- if (gBenchmarkStartTimeNs == 0) {
- gBenchmarkStartTimeNs = NanoTime;
- }
-}
-
-int main(int argc, char* argv[]) {
- if (gBenchmarks->empty()) {
- fprintf(stderr, "No benchmarks registered!\n");
- exit(EXIT_FAILURE);
- }
-
- bool need_header = true;
- for (auto b : *gBenchmarks) {
- if (ShouldRun(b, argc, argv)) {
- if (need_header) {
- printf("%-25s %10s %10s\n", "", "iterations", "ns/op");
- fflush(stdout);
- need_header = false;
- }
- Run(b);
- }
- }
-
- if (need_header) {
- fprintf(stderr, "No matching benchmarks!\n");
- fprintf(stderr, "Available benchmarks:\n");
- for (auto b : *gBenchmarks) {
- fprintf(stderr, " %s\n", b->Name());
- }
- exit(EXIT_FAILURE);
- }
-
- return 0;
-}
diff --git a/liblog/tests/liblog_benchmark.cpp b/liblog/tests/liblog_benchmark.cpp
index c4bf959..c2f3f83 100644
--- a/liblog/tests/liblog_benchmark.cpp
+++ b/liblog/tests/liblog_benchmark.cpp
@@ -19,18 +19,20 @@
#include <poll.h>
#include <sys/endian.h>
#include <sys/socket.h>
+#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>
#include <unordered_set>
#include <android-base/file.h>
+#include <benchmark/benchmark.h>
#include <cutils/sockets.h>
#include <log/event_tag_map.h>
#include <log/log_transport.h>
#include <private/android_logger.h>
-#include "benchmark.h"
+BENCHMARK_MAIN();
// enhanced version of LOG_FAILURE_RETRY to add support for EAGAIN and
// non-syscall libs. Since we are benchmarking, or using this in the emergency
@@ -51,15 +53,11 @@
* the log at high pressure. Expect this to be less than double the process
* wakeup time (2ms?)
*/
-static void BM_log_maximum_retry(int iters) {
- StartBenchmarkTiming();
-
- for (int i = 0; i < iters; ++i) {
- LOG_FAILURE_RETRY(
- __android_log_print(ANDROID_LOG_INFO, "BM_log_maximum_retry", "%d", i));
+static void BM_log_maximum_retry(benchmark::State& state) {
+ while (state.KeepRunning()) {
+ LOG_FAILURE_RETRY(__android_log_print(
+ ANDROID_LOG_INFO, "BM_log_maximum_retry", "%zu", state.iterations()));
}
-
- StopBenchmarkTiming();
}
BENCHMARK(BM_log_maximum_retry);
@@ -68,14 +66,11 @@
* at high pressure. Expect this to be less than double the process wakeup
* time (2ms?)
*/
-static void BM_log_maximum(int iters) {
- StartBenchmarkTiming();
-
- for (int i = 0; i < iters; ++i) {
- __android_log_print(ANDROID_LOG_INFO, "BM_log_maximum", "%d", i);
+static void BM_log_maximum(benchmark::State& state) {
+ while (state.KeepRunning()) {
+ __android_log_print(ANDROID_LOG_INFO, "BM_log_maximum", "%zu",
+ state.iterations());
}
-
- StopBenchmarkTiming();
}
BENCHMARK(BM_log_maximum);
@@ -87,36 +82,98 @@
android_set_log_transport(LOGGER_DEFAULT);
}
-static void BM_log_maximum_null(int iters) {
+static void BM_log_maximum_null(benchmark::State& state) {
set_log_null();
- BM_log_maximum(iters);
+ BM_log_maximum(state);
set_log_default();
}
BENCHMARK(BM_log_maximum_null);
/*
* Measure the time it takes to collect the time using
- * discrete acquisition (StartBenchmarkTiming() -> StopBenchmarkTiming())
+ * discrete acquisition (state.PauseTiming() to state.ResumeTiming())
* under light load. Expect this to be a syscall period (2us) or
* data read time if zero-syscall.
*
* vdso support in the kernel and the library can allow
- * clock_gettime to be zero-syscall.
+ * clock_gettime to be zero-syscall, but there there does remain some
+ * benchmarking overhead to pause and resume; assumptions are both are
+ * covered.
*/
-static void BM_clock_overhead(int iters) {
- for (int i = 0; i < iters; ++i) {
- StartBenchmarkTiming();
- StopBenchmarkTiming();
+static void BM_clock_overhead(benchmark::State& state) {
+ while (state.KeepRunning()) {
+ state.PauseTiming();
+ state.ResumeTiming();
}
}
BENCHMARK(BM_clock_overhead);
+static void do_clock_overhead(benchmark::State& state, clockid_t clk_id) {
+ timespec t;
+ while (state.KeepRunning()) {
+ clock_gettime(clk_id, &t);
+ }
+}
+
+static void BM_time_clock_gettime_REALTIME(benchmark::State& state) {
+ do_clock_overhead(state, CLOCK_REALTIME);
+}
+BENCHMARK(BM_time_clock_gettime_REALTIME);
+
+static void BM_time_clock_gettime_MONOTONIC(benchmark::State& state) {
+ do_clock_overhead(state, CLOCK_MONOTONIC);
+}
+BENCHMARK(BM_time_clock_gettime_MONOTONIC);
+
+static void BM_time_clock_gettime_MONOTONIC_syscall(benchmark::State& state) {
+ timespec t;
+ while (state.KeepRunning()) {
+ syscall(__NR_clock_gettime, CLOCK_MONOTONIC, &t);
+ }
+}
+BENCHMARK(BM_time_clock_gettime_MONOTONIC_syscall);
+
+static void BM_time_clock_gettime_MONOTONIC_RAW(benchmark::State& state) {
+ do_clock_overhead(state, CLOCK_MONOTONIC_RAW);
+}
+BENCHMARK(BM_time_clock_gettime_MONOTONIC_RAW);
+
+static void BM_time_clock_gettime_BOOTTIME(benchmark::State& state) {
+ do_clock_overhead(state, CLOCK_BOOTTIME);
+}
+BENCHMARK(BM_time_clock_gettime_BOOTTIME);
+
+static void BM_time_clock_getres_MONOTONIC(benchmark::State& state) {
+ timespec t;
+ while (state.KeepRunning()) {
+ clock_getres(CLOCK_MONOTONIC, &t);
+ }
+}
+BENCHMARK(BM_time_clock_getres_MONOTONIC);
+
+static void BM_time_clock_getres_MONOTONIC_syscall(benchmark::State& state) {
+ timespec t;
+ while (state.KeepRunning()) {
+ syscall(__NR_clock_getres, CLOCK_MONOTONIC, &t);
+ }
+}
+BENCHMARK(BM_time_clock_getres_MONOTONIC_syscall);
+
+static void BM_time_time(benchmark::State& state) {
+ while (state.KeepRunning()) {
+ time_t now;
+ now = time(&now);
+ }
+}
+BENCHMARK(BM_time_time);
+
/*
* Measure the time it takes to submit the android logging data to pstore
*/
-static void BM_pmsg_short(int iters) {
+static void BM_pmsg_short(benchmark::State& state) {
int pstore_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY));
if (pstore_fd < 0) {
+ state.SkipWithError("/dev/pmsg0");
return;
}
@@ -175,13 +232,12 @@
newVec[2].iov_base = &buffer;
newVec[2].iov_len = sizeof(buffer);
- StartBenchmarkTiming();
- for (int i = 0; i < iters; ++i) {
+ while (state.KeepRunning()) {
++snapshot;
buffer.payload.data = htole32(snapshot);
writev(pstore_fd, newVec, nr);
}
- StopBenchmarkTiming();
+ state.PauseTiming();
close(pstore_fd);
}
BENCHMARK(BM_pmsg_short);
@@ -190,9 +246,10 @@
* Measure the time it takes to submit the android logging data to pstore
* best case aligned single block.
*/
-static void BM_pmsg_short_aligned(int iters) {
+static void BM_pmsg_short_aligned(benchmark::State& state) {
int pstore_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY));
if (pstore_fd < 0) {
+ state.SkipWithError("/dev/pmsg0");
return;
}
@@ -228,7 +285,8 @@
memset(buf, 0, sizeof(buf));
struct packet* buffer = (struct packet*)(((uintptr_t)buf + 7) & ~7);
if (((uintptr_t)&buffer->pmsg_header) & 7) {
- fprintf(stderr, "&buffer=0x%p iters=%d\n", &buffer->pmsg_header, iters);
+ fprintf(stderr, "&buffer=0x%p iterations=%zu\n", &buffer->pmsg_header,
+ state.iterations());
}
buffer->pmsg_header.magic = LOGGER_MAGIC;
@@ -247,15 +305,14 @@
uint32_t snapshot = 0;
buffer->payload.payload.data = htole32(snapshot);
- StartBenchmarkTiming();
- for (int i = 0; i < iters; ++i) {
+ while (state.KeepRunning()) {
++snapshot;
buffer->payload.payload.data = htole32(snapshot);
write(pstore_fd, &buffer->pmsg_header,
sizeof(android_pmsg_log_header_t) + sizeof(android_log_header_t) +
sizeof(android_log_event_int_t));
}
- StopBenchmarkTiming();
+ state.PauseTiming();
close(pstore_fd);
}
BENCHMARK(BM_pmsg_short_aligned);
@@ -264,9 +321,10 @@
* Measure the time it takes to submit the android logging data to pstore
* best case aligned single block.
*/
-static void BM_pmsg_short_unaligned1(int iters) {
+static void BM_pmsg_short_unaligned1(benchmark::State& state) {
int pstore_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY));
if (pstore_fd < 0) {
+ state.SkipWithError("/dev/pmsg0");
return;
}
@@ -302,7 +360,8 @@
memset(buf, 0, sizeof(buf));
struct packet* buffer = (struct packet*)((((uintptr_t)buf + 7) & ~7) + 1);
if ((((uintptr_t)&buffer->pmsg_header) & 7) != 1) {
- fprintf(stderr, "&buffer=0x%p iters=%d\n", &buffer->pmsg_header, iters);
+ fprintf(stderr, "&buffer=0x%p iterations=%zu\n", &buffer->pmsg_header,
+ state.iterations());
}
buffer->pmsg_header.magic = LOGGER_MAGIC;
@@ -321,15 +380,14 @@
uint32_t snapshot = 0;
buffer->payload.payload.data = htole32(snapshot);
- StartBenchmarkTiming();
- for (int i = 0; i < iters; ++i) {
+ while (state.KeepRunning()) {
++snapshot;
buffer->payload.payload.data = htole32(snapshot);
write(pstore_fd, &buffer->pmsg_header,
sizeof(android_pmsg_log_header_t) + sizeof(android_log_header_t) +
sizeof(android_log_event_int_t));
}
- StopBenchmarkTiming();
+ state.PauseTiming();
close(pstore_fd);
}
BENCHMARK(BM_pmsg_short_unaligned1);
@@ -338,9 +396,10 @@
* Measure the time it takes to submit the android logging data to pstore
* best case aligned single block.
*/
-static void BM_pmsg_long_aligned(int iters) {
+static void BM_pmsg_long_aligned(benchmark::State& state) {
int pstore_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY));
if (pstore_fd < 0) {
+ state.SkipWithError("/dev/pmsg0");
return;
}
@@ -376,7 +435,8 @@
memset(buf, 0, sizeof(buf));
struct packet* buffer = (struct packet*)(((uintptr_t)buf + 7) & ~7);
if (((uintptr_t)&buffer->pmsg_header) & 7) {
- fprintf(stderr, "&buffer=0x%p iters=%d\n", &buffer->pmsg_header, iters);
+ fprintf(stderr, "&buffer=0x%p iterations=%zu\n", &buffer->pmsg_header,
+ state.iterations());
}
buffer->pmsg_header.magic = LOGGER_MAGIC;
@@ -395,13 +455,12 @@
uint32_t snapshot = 0;
buffer->payload.payload.data = htole32(snapshot);
- StartBenchmarkTiming();
- for (int i = 0; i < iters; ++i) {
+ while (state.KeepRunning()) {
++snapshot;
buffer->payload.payload.data = htole32(snapshot);
write(pstore_fd, &buffer->pmsg_header, LOGGER_ENTRY_MAX_PAYLOAD);
}
- StopBenchmarkTiming();
+ state.PauseTiming();
close(pstore_fd);
}
BENCHMARK(BM_pmsg_long_aligned);
@@ -410,9 +469,10 @@
* Measure the time it takes to submit the android logging data to pstore
* best case aligned single block.
*/
-static void BM_pmsg_long_unaligned1(int iters) {
+static void BM_pmsg_long_unaligned1(benchmark::State& state) {
int pstore_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY));
if (pstore_fd < 0) {
+ state.SkipWithError("/dev/pmsg0");
return;
}
@@ -448,7 +508,8 @@
memset(buf, 0, sizeof(buf));
struct packet* buffer = (struct packet*)((((uintptr_t)buf + 7) & ~7) + 1);
if ((((uintptr_t)&buffer->pmsg_header) & 7) != 1) {
- fprintf(stderr, "&buffer=0x%p iters=%d\n", &buffer->pmsg_header, iters);
+ fprintf(stderr, "&buffer=0x%p iterations=%zu\n", &buffer->pmsg_header,
+ state.iterations());
}
buffer->pmsg_header.magic = LOGGER_MAGIC;
@@ -467,22 +528,20 @@
uint32_t snapshot = 0;
buffer->payload.payload.data = htole32(snapshot);
- StartBenchmarkTiming();
- for (int i = 0; i < iters; ++i) {
+ while (state.KeepRunning()) {
++snapshot;
buffer->payload.payload.data = htole32(snapshot);
write(pstore_fd, &buffer->pmsg_header, LOGGER_ENTRY_MAX_PAYLOAD);
}
- StopBenchmarkTiming();
+ state.PauseTiming();
close(pstore_fd);
}
BENCHMARK(BM_pmsg_long_unaligned1);
/*
* Measure the time it takes to form sprintf plus time using
- * discrete acquisition (StartBenchmarkTiming() -> StopBenchmarkTiming())
- * under light load. Expect this to be a syscall period (2us) or sprintf
- * time if zero-syscall time.
+ * discrete acquisition under light load. Expect this to be a syscall period
+ * (2us) or sprintf time if zero-syscall time.
*/
/* helper function */
static void test_print(const char* fmt, ...) {
@@ -498,58 +557,55 @@
#define logd_sleep() usleep(50) // really allow logd to catch up
/* performance test */
-static void BM_sprintf_overhead(int iters) {
- for (int i = 0; i < iters; ++i) {
- StartBenchmarkTiming();
- test_print("BM_sprintf_overhead:%d", i);
- StopBenchmarkTiming();
+static void BM_sprintf_overhead(benchmark::State& state) {
+ while (state.KeepRunning()) {
+ test_print("BM_sprintf_overhead:%zu", state.iterations());
+ state.PauseTiming();
logd_yield();
+ state.ResumeTiming();
}
}
BENCHMARK(BM_sprintf_overhead);
/*
* Measure the time it takes to submit the android printing logging call
- * using discrete acquisition discrete acquisition (StartBenchmarkTiming() ->
- * StopBenchmarkTiming()) under light load. Expect this to be a dozen or so
- * syscall periods (40us) plus time to run *printf
+ * using discrete acquisition discrete acquisition under light load. Expect
+ * this to be a dozen or so syscall periods (40us) plus time to run *printf
*/
-static void BM_log_print_overhead(int iters) {
- for (int i = 0; i < iters; ++i) {
- StartBenchmarkTiming();
- __android_log_print(ANDROID_LOG_INFO, "BM_log_overhead", "%d", i);
- StopBenchmarkTiming();
+static void BM_log_print_overhead(benchmark::State& state) {
+ while (state.KeepRunning()) {
+ __android_log_print(ANDROID_LOG_INFO, "BM_log_overhead", "%zu",
+ state.iterations());
+ state.PauseTiming();
logd_yield();
+ state.ResumeTiming();
}
}
BENCHMARK(BM_log_print_overhead);
/*
* Measure the time it takes to submit the android event logging call
- * using discrete acquisition (StartBenchmarkTiming() -> StopBenchmarkTiming())
- * under light load. Expect this to be a long path to logger to convert the
- * unknown tag (0) into a tagname (less than 200us).
+ * using discrete acquisition under light load. Expect this to be a long path
+ * to logger to convert the unknown tag (0) into a tagname (less than 200us).
*/
-static void BM_log_event_overhead(int iters) {
- for (unsigned long long i = 0; i < (unsigned)iters; ++i) {
- StartBenchmarkTiming();
+static void BM_log_event_overhead(benchmark::State& state) {
+ for (int64_t i = 0; state.KeepRunning(); ++i) {
// log tag number 0 is not known, nor shall it ever be known
__android_log_btwrite(0, EVENT_TYPE_LONG, &i, sizeof(i));
- StopBenchmarkTiming();
+ state.PauseTiming();
logd_yield();
+ state.ResumeTiming();
}
}
BENCHMARK(BM_log_event_overhead);
/*
* Measure the time it takes to submit the android event logging call
- * using discrete acquisition (StartBenchmarkTiming() -> StopBenchmarkTiming())
- * under light load with a known logtag. Expect this to be a dozen or so
- * syscall periods (less than 40us)
+ * using discrete acquisition under light load with a known logtag. Expect
+ * this to be a dozen or so syscall periods (less than 40us)
*/
-static void BM_log_event_overhead_42(int iters) {
- for (unsigned long long i = 0; i < (unsigned)iters; ++i) {
- StartBenchmarkTiming();
+static void BM_log_event_overhead_42(benchmark::State& state) {
+ for (int64_t i = 0; state.KeepRunning(); ++i) {
// In system/core/logcat/event.logtags:
// # These are used for testing, do not modify without updating
// # tests/framework-tests/src/android/util/EventLogFunctionalTest.java.
@@ -557,40 +613,42 @@
// # system/core/liblog/tests/liblog_test.cpp
// 42 answer (to life the universe etc|3)
__android_log_btwrite(42, EVENT_TYPE_LONG, &i, sizeof(i));
- StopBenchmarkTiming();
+ state.PauseTiming();
logd_yield();
+ state.ResumeTiming();
}
}
BENCHMARK(BM_log_event_overhead_42);
-static void BM_log_event_overhead_null(int iters) {
+static void BM_log_event_overhead_null(benchmark::State& state) {
set_log_null();
- BM_log_event_overhead(iters);
+ BM_log_event_overhead(state);
set_log_default();
}
BENCHMARK(BM_log_event_overhead_null);
/*
* Measure the time it takes to submit the android event logging call
- * using discrete acquisition (StartBenchmarkTiming() -> StopBenchmarkTiming())
- * under very-light load (<1% CPU utilization).
+ * using discrete acquisition under very-light load (<1% CPU utilization).
*/
-static void BM_log_light_overhead(int iters) {
- for (unsigned long long i = 0; i < (unsigned)iters; ++i) {
- StartBenchmarkTiming();
+static void BM_log_light_overhead(benchmark::State& state) {
+ for (int64_t i = 0; state.KeepRunning(); ++i) {
__android_log_btwrite(0, EVENT_TYPE_LONG, &i, sizeof(i));
- StopBenchmarkTiming();
+ state.PauseTiming();
usleep(10000);
+ state.ResumeTiming();
}
}
BENCHMARK(BM_log_light_overhead);
-static void BM_log_light_overhead_null(int iters) {
+static void BM_log_light_overhead_null(benchmark::State& state) {
set_log_null();
- BM_log_light_overhead(iters);
+ BM_log_light_overhead(state);
set_log_default();
}
-BENCHMARK(BM_log_light_overhead_null);
+// Default gets out of hand for this test, so we set a reasonable number of
+// iterations for a timely result.
+BENCHMARK(BM_log_light_overhead_null)->Iterations(500);
static void caught_latency(int /*signum*/) {
unsigned long long v = 0xDEADBEEFA55A5AA5ULL;
@@ -614,10 +672,12 @@
/*
* Measure the time it takes for the logd posting call to acquire the
- * timestamp to place into the internal record. Expect this to be less than
- * 4 syscalls (3us).
+ * timestamp to place into the internal record. Expect this to be less than
+ * 4 syscalls (3us). This test uses manual injection of timing because it is
+ * comparing the timestamp at send, and then picking up the corresponding log
+ * end-to-end long path from logd to see what actual timestamp was submitted.
*/
-static void BM_log_latency(int iters) {
+static void BM_log_latency(benchmark::State& state) {
pid_t pid = getpid();
struct logger_list* logger_list =
@@ -631,7 +691,8 @@
signal(SIGALRM, caught_latency);
alarm(alarm_time);
- for (int j = 0, i = 0; i < iters && j < 10 * iters; ++i, ++j) {
+ for (size_t j = 0; state.KeepRunning() && j < 10 * state.iterations(); ++j) {
+ retry: // We allow transitory errors (logd overloaded) to be retried.
log_time ts;
LOG_FAILURE_RETRY((ts = log_time(CLOCK_REALTIME),
android_btWriteLog(0, EVENT_TYPE_LONG, &ts, sizeof(ts))));
@@ -642,7 +703,7 @@
alarm(alarm_time);
if (ret <= 0) {
- iters = i;
+ state.SkipWithError("android_logger_list_read");
break;
}
if ((log_msg.entry.len != (4 + 1 + 8)) ||
@@ -658,7 +719,7 @@
log_time tx(eventData + 4 + 1);
if (ts != tx) {
if (0xDEADBEEFA55A5AA5ULL == caught_convert(eventData + 4 + 1)) {
- iters = i;
+ state.SkipWithError("signal");
break;
}
continue;
@@ -666,12 +727,8 @@
uint64_t start = ts.nsec();
uint64_t end = log_msg.nsec();
- if (end >= start) {
- StartBenchmarkTiming(start);
- StopBenchmarkTiming(end);
- } else {
- --i;
- }
+ if (end < start) goto retry;
+ state.SetIterationTime((end - start) / (double)NS_PER_SEC);
break;
}
}
@@ -681,7 +738,9 @@
android_logger_list_free(logger_list);
}
-BENCHMARK(BM_log_latency);
+// Default gets out of hand for this test, so we set a reasonable number of
+// iterations for a timely result.
+BENCHMARK(BM_log_latency)->UseManualTime()->Iterations(200);
static void caught_delay(int /*signum*/) {
unsigned long long v = 0xDEADBEEFA55A5AA6ULL;
@@ -693,7 +752,7 @@
* Measure the time it takes for the logd posting call to make it into
* the logs. Expect this to be less than double the process wakeup time (2ms).
*/
-static void BM_log_delay(int iters) {
+static void BM_log_delay(benchmark::State& state) {
pid_t pid = getpid();
struct logger_list* logger_list =
@@ -707,9 +766,7 @@
signal(SIGALRM, caught_delay);
alarm(alarm_time);
- StartBenchmarkTiming();
-
- for (int i = 0; i < iters; ++i) {
+ while (state.KeepRunning()) {
log_time ts(CLOCK_REALTIME);
LOG_FAILURE_RETRY(android_btWriteLog(0, EVENT_TYPE_LONG, &ts, sizeof(ts)));
@@ -720,7 +777,7 @@
alarm(alarm_time);
if (ret <= 0) {
- iters = i;
+ state.SkipWithError("android_logger_list_read");
break;
}
if ((log_msg.entry.len != (4 + 1 + 8)) ||
@@ -736,7 +793,7 @@
log_time tx(eventData + 4 + 1);
if (ts != tx) {
if (0xDEADBEEFA55A5AA6ULL == caught_convert(eventData + 4 + 1)) {
- iters = i;
+ state.SkipWithError("signal");
break;
}
continue;
@@ -745,12 +802,11 @@
break;
}
}
+ state.PauseTiming();
signal(SIGALRM, SIG_DFL);
alarm(0);
- StopBenchmarkTiming();
-
android_logger_list_free(logger_list);
}
BENCHMARK(BM_log_delay);
@@ -758,45 +814,33 @@
/*
* Measure the time it takes for __android_log_is_loggable.
*/
-static void BM_is_loggable(int iters) {
+static void BM_is_loggable(benchmark::State& state) {
static const char logd[] = "logd";
- StartBenchmarkTiming();
-
- for (int i = 0; i < iters; ++i) {
+ while (state.KeepRunning()) {
__android_log_is_loggable_len(ANDROID_LOG_WARN, logd, strlen(logd),
ANDROID_LOG_VERBOSE);
}
-
- StopBenchmarkTiming();
}
BENCHMARK(BM_is_loggable);
/*
* Measure the time it takes for android_log_clockid.
*/
-static void BM_clockid(int iters) {
- StartBenchmarkTiming();
-
- for (int i = 0; i < iters; ++i) {
+static void BM_clockid(benchmark::State& state) {
+ while (state.KeepRunning()) {
android_log_clockid();
}
-
- StopBenchmarkTiming();
}
BENCHMARK(BM_clockid);
/*
* Measure the time it takes for __android_log_security.
*/
-static void BM_security(int iters) {
- StartBenchmarkTiming();
-
- for (int i = 0; i < iters; ++i) {
+static void BM_security(benchmark::State& state) {
+ while (state.KeepRunning()) {
__android_log_security();
}
-
- StopBenchmarkTiming();
}
BENCHMARK(BM_security);
@@ -824,21 +868,17 @@
/*
* Measure the time it takes for android_lookupEventTag_len
*/
-static void BM_lookupEventTag(int iters) {
+static void BM_lookupEventTag(benchmark::State& state) {
prechargeEventMap();
std::unordered_set<uint32_t>::const_iterator it = set.begin();
- StartBenchmarkTiming();
-
- for (int i = 0; i < iters; ++i) {
+ while (state.KeepRunning()) {
size_t len;
android_lookupEventTag_len(map, &len, (*it));
++it;
if (it == set.end()) it = set.begin();
}
-
- StopBenchmarkTiming();
}
BENCHMARK(BM_lookupEventTag);
@@ -847,7 +887,7 @@
*/
static uint32_t notTag = 1;
-static void BM_lookupEventTag_NOT(int iters) {
+static void BM_lookupEventTag_NOT(benchmark::State& state) {
prechargeEventMap();
while (set.find(notTag) != set.end()) {
@@ -855,15 +895,11 @@
if (notTag >= USHRT_MAX) notTag = 1;
}
- StartBenchmarkTiming();
-
- for (int i = 0; i < iters; ++i) {
+ while (state.KeepRunning()) {
size_t len;
android_lookupEventTag_len(map, &len, notTag);
}
- StopBenchmarkTiming();
-
++notTag;
if (notTag >= USHRT_MAX) notTag = 1;
}
@@ -872,42 +908,38 @@
/*
* Measure the time it takes for android_lookupEventFormat_len
*/
-static void BM_lookupEventFormat(int iters) {
+static void BM_lookupEventFormat(benchmark::State& state) {
prechargeEventMap();
std::unordered_set<uint32_t>::const_iterator it = set.begin();
- StartBenchmarkTiming();
-
- for (int i = 0; i < iters; ++i) {
+ while (state.KeepRunning()) {
size_t len;
android_lookupEventFormat_len(map, &len, (*it));
++it;
if (it == set.end()) it = set.begin();
}
-
- StopBenchmarkTiming();
}
BENCHMARK(BM_lookupEventFormat);
/*
* Measure the time it takes for android_lookupEventTagNum plus above
*/
-static void BM_lookupEventTagNum(int iters) {
+static void BM_lookupEventTagNum(benchmark::State& state) {
prechargeEventMap();
std::unordered_set<uint32_t>::const_iterator it = set.begin();
- for (int i = 0; i < iters; ++i) {
+ while (state.KeepRunning()) {
size_t len;
const char* name = android_lookupEventTag_len(map, &len, (*it));
std::string Name(name, len);
const char* format = android_lookupEventFormat_len(map, &len, (*it));
std::string Format(format, len);
- StartBenchmarkTiming();
+ state.ResumeTiming();
android_lookupEventTagNum(map, Name.c_str(), Format.c_str(),
ANDROID_LOG_UNKNOWN);
- StopBenchmarkTiming();
+ state.PauseTiming();
++it;
if (it == set.end()) it = set.begin();
}
@@ -943,7 +975,7 @@
close(sock);
}
-static void BM_lookupEventTagNum_logd_new(int iters) {
+static void BM_lookupEventTagNum_logd_new(benchmark::State& state) {
fprintf(stderr,
"WARNING: "
"This test can cause logd to grow in size and hit DOS limiter\n");
@@ -965,7 +997,7 @@
data_event_log_tags = empty_event_log_tags;
}
- for (int i = 0; i < iters; ++i) {
+ while (state.KeepRunning()) {
char buffer[256];
memset(buffer, 0, sizeof(buffer));
log_time now(CLOCK_MONOTONIC);
@@ -973,9 +1005,9 @@
snprintf(name, sizeof(name), "a%" PRIu64, now.nsec());
snprintf(buffer, sizeof(buffer), "getEventTag name=%s format=\"(new|1)\"",
name);
- StartBenchmarkTiming();
+ state.ResumeTiming();
send_to_control(buffer, sizeof(buffer));
- StopBenchmarkTiming();
+ state.PauseTiming();
}
// Restore copies (logd still know about them, until crash or reboot)
@@ -1002,12 +1034,12 @@
}
BENCHMARK(BM_lookupEventTagNum_logd_new);
-static void BM_lookupEventTagNum_logd_existing(int iters) {
+static void BM_lookupEventTagNum_logd_existing(benchmark::State& state) {
prechargeEventMap();
std::unordered_set<uint32_t>::const_iterator it = set.begin();
- for (int i = 0; i < iters; ++i) {
+ while (state.KeepRunning()) {
size_t len;
const char* name = android_lookupEventTag_len(map, &len, (*it));
std::string Name(name, len);
@@ -1018,9 +1050,9 @@
snprintf(buffer, sizeof(buffer), "getEventTag name=%s format=\"%s\"",
Name.c_str(), Format.c_str());
- StartBenchmarkTiming();
+ state.ResumeTiming();
send_to_control(buffer, sizeof(buffer));
- StopBenchmarkTiming();
+ state.PauseTiming();
++it;
if (it == set.end()) it = set.begin();
}
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index 133f3b9..9389b40 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -55,6 +55,7 @@
"Elf.cpp",
"ElfInterface.cpp",
"ElfInterfaceArm.cpp",
+ "JitDebug.cpp",
"Log.cpp",
"MapInfo.cpp",
"Maps.cpp",
@@ -128,6 +129,7 @@
"tests/ElfInterfaceTest.cpp",
"tests/ElfTest.cpp",
"tests/ElfTestUtils.cpp",
+ "tests/JitDebugTest.cpp",
"tests/LogFake.cpp",
"tests/MapInfoGetElfTest.cpp",
"tests/MapInfoGetLoadBiasTest.cpp",
@@ -168,6 +170,8 @@
data: [
"tests/files/elf32.xz",
"tests/files/elf64.xz",
+ "tests/files/offline/jit_debug_arm32/*",
+ "tests/files/offline/jit_debug_x86_32/*",
"tests/files/offline/gnu_debugdata_arm32/*",
"tests/files/offline/straddle_arm32/*",
"tests/files/offline/straddle_arm64/*",
diff --git a/libunwindstack/DwarfSection.cpp b/libunwindstack/DwarfSection.cpp
index 0954187..91d855b 100644
--- a/libunwindstack/DwarfSection.cpp
+++ b/libunwindstack/DwarfSection.cpp
@@ -34,6 +34,8 @@
namespace unwindstack {
+constexpr uint64_t DEX_PC_REG = 0x20444558;
+
DwarfSection::DwarfSection(Memory* memory) : memory_(memory), last_error_(DWARF_ERROR_NONE) {}
const DwarfFde* DwarfSection::GetFdeFromPc(uint64_t pc) {
@@ -98,6 +100,84 @@
}
template <typename AddressType>
+struct EvalInfo {
+ const dwarf_loc_regs_t* loc_regs;
+ const DwarfCie* cie;
+ RegsImpl<AddressType>* cur_regs;
+ Memory* regular_memory;
+ AddressType cfa;
+ bool return_address_undefined = false;
+ uint64_t reg_map = 0;
+ AddressType reg_values[64];
+};
+
+template <typename AddressType>
+bool DwarfSectionImpl<AddressType>::EvalRegister(const DwarfLocation* loc, uint32_t reg,
+ AddressType* reg_ptr, void* info) {
+ EvalInfo<AddressType>* eval_info = reinterpret_cast<EvalInfo<AddressType>*>(info);
+ Memory* regular_memory = eval_info->regular_memory;
+ switch (loc->type) {
+ case DWARF_LOCATION_OFFSET:
+ if (!regular_memory->ReadFully(eval_info->cfa + loc->values[0], reg_ptr, sizeof(AddressType))) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ break;
+ case DWARF_LOCATION_VAL_OFFSET:
+ *reg_ptr = eval_info->cfa + loc->values[0];
+ break;
+ case DWARF_LOCATION_REGISTER: {
+ uint32_t cur_reg = loc->values[0];
+ if (cur_reg >= eval_info->cur_regs->total_regs()) {
+ last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+ return false;
+ }
+ AddressType* cur_reg_ptr = &(*eval_info->cur_regs)[cur_reg];
+ const auto& entry = eval_info->loc_regs->find(cur_reg);
+ if (entry != eval_info->loc_regs->end()) {
+ if (!(eval_info->reg_map & (1 << cur_reg))) {
+ eval_info->reg_map |= 1 << cur_reg;
+ eval_info->reg_values[cur_reg] = *cur_reg_ptr;
+ if (!EvalRegister(&entry->second, cur_reg, cur_reg_ptr, eval_info)) {
+ return false;
+ }
+ }
+
+ // Use the register value from before any evaluations.
+ *reg_ptr = eval_info->reg_values[cur_reg] + loc->values[1];
+ } else {
+ *reg_ptr = *cur_reg_ptr + loc->values[1];
+ }
+ break;
+ }
+ case DWARF_LOCATION_EXPRESSION:
+ case DWARF_LOCATION_VAL_EXPRESSION: {
+ AddressType value;
+ if (!EvalExpression(*loc, eval_info->cie->version, regular_memory, &value)) {
+ return false;
+ }
+ if (loc->type == DWARF_LOCATION_EXPRESSION) {
+ if (!regular_memory->ReadFully(value, reg_ptr, sizeof(AddressType))) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ } else {
+ *reg_ptr = value;
+ }
+ break;
+ }
+ case DWARF_LOCATION_UNDEFINED:
+ if (reg == eval_info->cie->return_address_register) {
+ eval_info->return_address_undefined = true;
+ }
+ default:
+ break;
+ }
+
+ return true;
+}
+
+template <typename AddressType>
bool DwarfSectionImpl<AddressType>::Eval(const DwarfCie* cie, Memory* regular_memory,
const dwarf_loc_regs_t& loc_regs, Regs* regs,
bool* finished) {
@@ -114,9 +194,13 @@
return false;
}
+ // Always set the dex pc to zero when evaluating.
+ cur_regs->set_dex_pc(0);
+
AddressType prev_cfa = regs->sp();
- AddressType cfa;
+ EvalInfo<AddressType> eval_info{
+ .loc_regs = &loc_regs, .cie = cie, .regular_memory = regular_memory, .cur_regs = cur_regs};
const DwarfLocation* loc = &cfa_entry->second;
// Only a few location types are valid for the cfa.
switch (loc->type) {
@@ -129,11 +213,11 @@
// pointer register does not have any associated location
// information, use the current cfa value.
if (regs->sp_reg() == loc->values[0] && loc_regs.count(regs->sp_reg()) == 0) {
- cfa = prev_cfa;
+ eval_info.cfa = prev_cfa;
} else {
- cfa = (*cur_regs)[loc->values[0]];
+ eval_info.cfa = (*cur_regs)[loc->values[0]];
}
- cfa += loc->values[1];
+ eval_info.cfa += loc->values[1];
break;
case DWARF_LOCATION_EXPRESSION:
case DWARF_LOCATION_VAL_EXPRESSION: {
@@ -142,12 +226,12 @@
return false;
}
if (loc->type == DWARF_LOCATION_EXPRESSION) {
- if (!regular_memory->ReadFully(value, &cfa, sizeof(AddressType))) {
+ if (!regular_memory->ReadFully(value, &eval_info.cfa, sizeof(AddressType))) {
last_error_ = DWARF_ERROR_MEMORY_INVALID;
return false;
}
} else {
- cfa = value;
+ eval_info.cfa = value;
}
break;
}
@@ -156,81 +240,38 @@
return false;
}
- // This code is not guaranteed to work in cases where a register location
- // is a double indirection to the actual value. For example, if r3 is set
- // to r5 + 4, and r5 is set to CFA + 4, then this won't necessarily work
- // because it does not guarantee that r5 is evaluated before r3.
- // Check that this case does not exist, and error if it does.
- bool return_address_undefined = false;
for (const auto& entry : loc_regs) {
- uint16_t reg = entry.first;
+ uint32_t reg = entry.first;
// Already handled the CFA register.
if (reg == CFA_REG) continue;
- if (reg >= cur_regs->total_regs()) {
- // Skip this unknown register.
+ AddressType* reg_ptr;
+ AddressType dex_pc = 0;
+ if (reg == DEX_PC_REG) {
+ // Special register that indicates this is a dex pc.
+ dex_pc = 0;
+ reg_ptr = &dex_pc;
+ } else if (reg >= cur_regs->total_regs() || eval_info.reg_map & (1 << reg)) {
+ // Skip this unknown register, or a register that has already been
+ // processed.
continue;
+ } else {
+ reg_ptr = &(*cur_regs)[reg];
+ eval_info.reg_map |= 1 << reg;
+ eval_info.reg_values[reg] = *reg_ptr;
}
- const DwarfLocation* loc = &entry.second;
- switch (loc->type) {
- case DWARF_LOCATION_OFFSET:
- if (!regular_memory->ReadFully(cfa + loc->values[0], &(*cur_regs)[reg],
- sizeof(AddressType))) {
- last_error_ = DWARF_ERROR_MEMORY_INVALID;
- return false;
- }
- break;
- case DWARF_LOCATION_VAL_OFFSET:
- (*cur_regs)[reg] = cfa + loc->values[0];
- break;
- case DWARF_LOCATION_REGISTER: {
- uint16_t cur_reg = loc->values[0];
- if (cur_reg >= cur_regs->total_regs()) {
- last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
- return false;
- }
- if (loc_regs.find(cur_reg) != loc_regs.end()) {
- // This is a double indirection, a register definition references
- // another register which is also defined as something other
- // than a register.
- log(0,
- "Invalid indirection: register %d references register %d which is "
- "not a plain register.\n",
- reg, cur_reg);
- last_error_ = DWARF_ERROR_ILLEGAL_STATE;
- return false;
- }
- (*cur_regs)[reg] = (*cur_regs)[cur_reg] + loc->values[1];
- break;
- }
- case DWARF_LOCATION_EXPRESSION:
- case DWARF_LOCATION_VAL_EXPRESSION: {
- AddressType value;
- if (!EvalExpression(*loc, cie->version, regular_memory, &value)) {
- return false;
- }
- if (loc->type == DWARF_LOCATION_EXPRESSION) {
- if (!regular_memory->ReadFully(value, &(*cur_regs)[reg], sizeof(AddressType))) {
- last_error_ = DWARF_ERROR_MEMORY_INVALID;
- return false;
- }
- } else {
- (*cur_regs)[reg] = value;
- }
- break;
- }
- case DWARF_LOCATION_UNDEFINED:
- if (reg == cie->return_address_register) {
- return_address_undefined = true;
- }
- default:
- break;
+ if (!EvalRegister(&entry.second, reg, reg_ptr, &eval_info)) {
+ return false;
+ }
+
+ if (reg == DEX_PC_REG) {
+ cur_regs->set_dex_pc(dex_pc);
}
}
// Find the return address location.
- if (return_address_undefined) {
+ if (eval_info.return_address_undefined) {
cur_regs->set_pc(0);
} else {
cur_regs->set_pc((*cur_regs)[cie->return_address_register]);
@@ -239,7 +280,7 @@
// If the pc was set to zero, consider this the final frame.
*finished = (cur_regs->pc() == 0) ? true : false;
- cur_regs->set_sp(cfa);
+ cur_regs->set_sp(eval_info.cfa);
return true;
}
diff --git a/libunwindstack/Elf.cpp b/libunwindstack/Elf.cpp
index 5ec4a3d..220e549 100644
--- a/libunwindstack/Elf.cpp
+++ b/libunwindstack/Elf.cpp
@@ -103,6 +103,37 @@
addr, load_bias_, name, func_offset)));
}
+bool Elf::GetGlobalVariable(const std::string& name, uint64_t* memory_address) {
+ if (!valid_) {
+ return false;
+ }
+
+ if (!interface_->GetGlobalVariable(name, memory_address) &&
+ (gnu_debugdata_interface_ == nullptr ||
+ !gnu_debugdata_interface_->GetGlobalVariable(name, memory_address))) {
+ return false;
+ }
+
+ // Adjust by the load bias.
+ if (*memory_address < load_bias_) {
+ return false;
+ }
+
+ *memory_address -= load_bias_;
+
+ // If this winds up in the dynamic section, then we might need to adjust
+ // the address.
+ uint64_t dynamic_end = interface_->dynamic_vaddr() + interface_->dynamic_size();
+ if (*memory_address >= interface_->dynamic_vaddr() && *memory_address < dynamic_end) {
+ if (interface_->dynamic_vaddr() > interface_->dynamic_offset()) {
+ *memory_address -= interface_->dynamic_vaddr() - interface_->dynamic_offset();
+ } else {
+ *memory_address += interface_->dynamic_offset() - interface_->dynamic_vaddr();
+ }
+ }
+ return true;
+}
+
// The relative pc is always relative to the start of the map from which it comes.
bool Elf::Step(uint64_t rel_pc, uint64_t adjusted_rel_pc, uint64_t elf_offset, Regs* regs,
Memory* process_memory, bool* finished) {
@@ -160,6 +191,23 @@
}
}
+bool Elf::IsValidPc(uint64_t pc) {
+ if (!valid_ || pc < load_bias_) {
+ return false;
+ }
+ pc -= load_bias_;
+
+ if (interface_->IsValidPc(pc)) {
+ return true;
+ }
+
+ if (gnu_debugdata_interface_ != nullptr && gnu_debugdata_interface_->IsValidPc(pc)) {
+ return true;
+ }
+
+ return false;
+}
+
ElfInterface* Elf::CreateInterfaceFromMemory(Memory* memory) {
if (!IsValidElf(memory)) {
return nullptr;
diff --git a/libunwindstack/ElfInterface.cpp b/libunwindstack/ElfInterface.cpp
index df1642e..0e3ab2c 100644
--- a/libunwindstack/ElfInterface.cpp
+++ b/libunwindstack/ElfInterface.cpp
@@ -43,6 +43,30 @@
}
}
+bool ElfInterface::IsValidPc(uint64_t pc) {
+ if (!pt_loads_.empty()) {
+ for (auto& entry : pt_loads_) {
+ uint64_t start = entry.second.table_offset;
+ uint64_t end = start + entry.second.table_size;
+ if (pc >= start && pc < end) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // No PT_LOAD data, look for a fde for this pc in the section data.
+ if (debug_frame_ != nullptr && debug_frame_->GetFdeFromPc(pc) != nullptr) {
+ return true;
+ }
+
+ if (eh_frame_ != nullptr && eh_frame_->GetFdeFromPc(pc) != nullptr) {
+ return true;
+ }
+
+ return false;
+}
+
Memory* ElfInterface::CreateGnuDebugdataMemory() {
if (gnu_debugdata_offset_ == 0 || gnu_debugdata_size_ == 0) {
return nullptr;
@@ -225,6 +249,10 @@
return false;
}
dynamic_offset_ = phdr.p_offset;
+ if (!memory_->ReadField(offset, &phdr, &phdr.p_vaddr, sizeof(phdr.p_vaddr))) {
+ return false;
+ }
+ dynamic_vaddr_ = phdr.p_vaddr;
if (!memory_->ReadField(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) {
return false;
}
@@ -386,6 +414,20 @@
return false;
}
+template <typename SymType>
+bool ElfInterface::GetGlobalVariableWithTemplate(const std::string& name, uint64_t* memory_address) {
+ if (symbols_.empty()) {
+ return false;
+ }
+
+ for (const auto symbol : symbols_) {
+ if (symbol->GetGlobal<SymType>(memory_, name, memory_address)) {
+ return true;
+ }
+ }
+ return false;
+}
+
bool ElfInterface::Step(uint64_t pc, uint64_t load_bias, Regs* regs, Memory* process_memory,
bool* finished) {
// Adjust the load bias to get the real relative pc.
@@ -451,6 +493,9 @@
template bool ElfInterface::GetFunctionNameWithTemplate<Elf64_Sym>(uint64_t, uint64_t, std::string*,
uint64_t*);
+template bool ElfInterface::GetGlobalVariableWithTemplate<Elf32_Sym>(const std::string&, uint64_t*);
+template bool ElfInterface::GetGlobalVariableWithTemplate<Elf64_Sym>(const std::string&, uint64_t*);
+
template void ElfInterface::GetMaxSizeWithTemplate<Elf32_Ehdr>(Memory*, uint64_t*);
template void ElfInterface::GetMaxSizeWithTemplate<Elf64_Ehdr>(Memory*, uint64_t*);
diff --git a/libunwindstack/JitDebug.cpp b/libunwindstack/JitDebug.cpp
new file mode 100644
index 0000000..d1dc0e6
--- /dev/null
+++ b/libunwindstack/JitDebug.cpp
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2017 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 <stdint.h>
+#include <sys/mman.h>
+
+#include <memory>
+#include <vector>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/JitDebug.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+
+// This implements the JIT Compilation Interface.
+// See https://sourceware.org/gdb/onlinedocs/gdb/JIT-Interface.html
+
+namespace unwindstack {
+
+struct JITCodeEntry32Pack {
+ uint32_t next;
+ uint32_t prev;
+ uint32_t symfile_addr;
+ uint64_t symfile_size;
+} __attribute__((packed));
+
+struct JITCodeEntry32Pad {
+ uint32_t next;
+ uint32_t prev;
+ uint32_t symfile_addr;
+ uint32_t pad;
+ uint64_t symfile_size;
+};
+
+struct JITCodeEntry64 {
+ uint64_t next;
+ uint64_t prev;
+ uint64_t symfile_addr;
+ uint64_t symfile_size;
+};
+
+struct JITDescriptorHeader {
+ uint32_t version;
+ uint32_t action_flag;
+};
+
+struct JITDescriptor32 {
+ JITDescriptorHeader header;
+ uint32_t relevant_entry;
+ uint32_t first_entry;
+};
+
+struct JITDescriptor64 {
+ JITDescriptorHeader header;
+ uint64_t relevant_entry;
+ uint64_t first_entry;
+};
+
+JitDebug::JitDebug(std::shared_ptr<Memory>& memory) : memory_(memory) {}
+
+JitDebug::JitDebug(std::shared_ptr<Memory>& memory, std::vector<std::string>& search_libs)
+ : memory_(memory), search_libs_(search_libs) {}
+
+JitDebug::~JitDebug() {
+ for (auto* elf : elf_list_) {
+ delete elf;
+ }
+}
+
+uint64_t JitDebug::ReadDescriptor32(uint64_t addr) {
+ JITDescriptor32 desc;
+ if (!memory_->ReadFully(addr, &desc, sizeof(desc))) {
+ return 0;
+ }
+
+ if (desc.header.version != 1 || desc.first_entry == 0) {
+ // Either unknown version, or no jit entries.
+ return 0;
+ }
+
+ return desc.first_entry;
+}
+
+uint64_t JitDebug::ReadDescriptor64(uint64_t addr) {
+ JITDescriptor64 desc;
+ if (!memory_->ReadFully(addr, &desc, sizeof(desc))) {
+ return 0;
+ }
+
+ if (desc.header.version != 1 || desc.first_entry == 0) {
+ // Either unknown version, or no jit entries.
+ return 0;
+ }
+
+ return desc.first_entry;
+}
+
+uint64_t JitDebug::ReadEntry32Pack(uint64_t* start, uint64_t* size) {
+ JITCodeEntry32Pack code;
+ if (!memory_->ReadFully(entry_addr_, &code, sizeof(code))) {
+ return 0;
+ }
+
+ *start = code.symfile_addr;
+ *size = code.symfile_size;
+ return code.next;
+}
+
+uint64_t JitDebug::ReadEntry32Pad(uint64_t* start, uint64_t* size) {
+ JITCodeEntry32Pad code;
+ if (!memory_->ReadFully(entry_addr_, &code, sizeof(code))) {
+ return 0;
+ }
+
+ *start = code.symfile_addr;
+ *size = code.symfile_size;
+ return code.next;
+}
+
+uint64_t JitDebug::ReadEntry64(uint64_t* start, uint64_t* size) {
+ JITCodeEntry64 code;
+ if (!memory_->ReadFully(entry_addr_, &code, sizeof(code))) {
+ return 0;
+ }
+
+ *start = code.symfile_addr;
+ *size = code.symfile_size;
+ return code.next;
+}
+
+void JitDebug::SetArch(ArchEnum arch) {
+ switch (arch) {
+ case ARCH_X86:
+ read_descriptor_func_ = &JitDebug::ReadDescriptor32;
+ read_entry_func_ = &JitDebug::ReadEntry32Pack;
+ break;
+
+ case ARCH_ARM:
+ case ARCH_MIPS:
+ read_descriptor_func_ = &JitDebug::ReadDescriptor32;
+ read_entry_func_ = &JitDebug::ReadEntry32Pad;
+ break;
+
+ case ARCH_ARM64:
+ case ARCH_X86_64:
+ case ARCH_MIPS64:
+ read_descriptor_func_ = &JitDebug::ReadDescriptor64;
+ read_entry_func_ = &JitDebug::ReadEntry64;
+ break;
+ case ARCH_UNKNOWN:
+ abort();
+ }
+}
+
+void JitDebug::Init(Maps* maps) {
+ if (initialized_) {
+ return;
+ }
+ // Regardless of what happens below, consider the init finished.
+ initialized_ = true;
+
+ std::string descriptor_name("__jit_debug_descriptor");
+ for (MapInfo* info : *maps) {
+ if (!(info->flags & PROT_EXEC) || !(info->flags & PROT_READ) || info->offset != 0) {
+ continue;
+ }
+
+ if (!search_libs_.empty()) {
+ bool found = false;
+ const char* lib = basename(info->name.c_str());
+ for (std::string& name : search_libs_) {
+ if (strcmp(name.c_str(), lib) == 0) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ continue;
+ }
+ }
+
+ Elf* elf = info->GetElf(memory_, true);
+ uint64_t descriptor_addr;
+ if (elf->GetGlobalVariable(descriptor_name, &descriptor_addr)) {
+ // Search for the first non-zero entry.
+ descriptor_addr += info->start;
+ entry_addr_ = (this->*read_descriptor_func_)(descriptor_addr);
+ if (entry_addr_ != 0) {
+ break;
+ }
+ }
+ }
+}
+
+Elf* JitDebug::GetElf(Maps* maps, uint64_t pc) {
+ // Use a single lock, this object should be used so infrequently that
+ // a fine grain lock is unnecessary.
+ std::lock_guard<std::mutex> guard(lock_);
+ if (!initialized_) {
+ Init(maps);
+ }
+
+ // Search the existing elf object first.
+ for (Elf* elf : elf_list_) {
+ if (elf->IsValidPc(pc)) {
+ return elf;
+ }
+ }
+
+ while (entry_addr_ != 0) {
+ uint64_t start;
+ uint64_t size;
+ entry_addr_ = (this->*read_entry_func_)(&start, &size);
+
+ Elf* elf = new Elf(new MemoryRange(memory_, start, size, 0));
+ elf->Init(true);
+ if (!elf->valid()) {
+ // The data is not formatted in a way we understand, do not attempt
+ // to process any other entries.
+ entry_addr_ = 0;
+ delete elf;
+ return nullptr;
+ }
+ elf_list_.push_back(elf);
+
+ if (elf->IsValidPc(pc)) {
+ return elf;
+ }
+ }
+ return nullptr;
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/Memory.cpp b/libunwindstack/Memory.cpp
index 285f879..d4ba680 100644
--- a/libunwindstack/Memory.cpp
+++ b/libunwindstack/Memory.cpp
@@ -345,4 +345,26 @@
return memory_->Read(addr, dst, size);
}
+MemoryOfflineParts::~MemoryOfflineParts() {
+ for (auto memory : memories_) {
+ delete memory;
+ }
+}
+
+size_t MemoryOfflineParts::Read(uint64_t addr, void* dst, size_t size) {
+ if (memories_.empty()) {
+ return 0;
+ }
+
+ // Do a read on each memory object, no support for reading across the
+ // different memory objects.
+ for (MemoryOffline* memory : memories_) {
+ size_t bytes = memory->Read(addr, dst, size);
+ if (bytes != 0) {
+ return bytes;
+ }
+ }
+ return 0;
+}
+
} // namespace unwindstack
diff --git a/libunwindstack/RegsArm.cpp b/libunwindstack/RegsArm.cpp
index 34f29bd..d05c3e2 100644
--- a/libunwindstack/RegsArm.cpp
+++ b/libunwindstack/RegsArm.cpp
@@ -37,10 +37,6 @@
}
uint64_t RegsArm::GetAdjustedPc(uint64_t rel_pc, Elf* elf) {
- if (!elf->valid()) {
- return rel_pc;
- }
-
uint64_t load_bias = elf->GetLoadBias();
if (rel_pc < load_bias) {
return rel_pc;
diff --git a/libunwindstack/Symbols.cpp b/libunwindstack/Symbols.cpp
index b4b92d6..25def40 100644
--- a/libunwindstack/Symbols.cpp
+++ b/libunwindstack/Symbols.cpp
@@ -108,8 +108,35 @@
return return_value;
}
+template <typename SymType>
+bool Symbols::GetGlobal(Memory* elf_memory, const std::string& name, uint64_t* memory_address) {
+ uint64_t cur_offset = offset_;
+ while (cur_offset + entry_size_ <= end_) {
+ SymType entry;
+ if (!elf_memory->ReadFully(cur_offset, &entry, sizeof(entry))) {
+ return false;
+ }
+ cur_offset += entry_size_;
+
+ if (entry.st_shndx != SHN_UNDEF && ELF32_ST_TYPE(entry.st_info) == STT_OBJECT &&
+ ELF32_ST_BIND(entry.st_info) == STB_GLOBAL) {
+ uint64_t str_offset = str_offset_ + entry.st_name;
+ if (str_offset < str_end_) {
+ std::string symbol;
+ if (elf_memory->ReadString(str_offset, &symbol, str_end_ - str_offset) && symbol == name) {
+ *memory_address = entry.st_value;
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
// Instantiate all of the needed template functions.
template bool Symbols::GetName<Elf32_Sym>(uint64_t, uint64_t, Memory*, std::string*, uint64_t*);
template bool Symbols::GetName<Elf64_Sym>(uint64_t, uint64_t, Memory*, std::string*, uint64_t*);
+template bool Symbols::GetGlobal<Elf32_Sym>(Memory*, const std::string&, uint64_t*);
+template bool Symbols::GetGlobal<Elf64_Sym>(Memory*, const std::string&, uint64_t*);
} // namespace unwindstack
diff --git a/libunwindstack/Symbols.h b/libunwindstack/Symbols.h
index 689144b..7d239c1 100644
--- a/libunwindstack/Symbols.h
+++ b/libunwindstack/Symbols.h
@@ -47,6 +47,9 @@
bool GetName(uint64_t addr, uint64_t load_bias, Memory* elf_memory, std::string* name,
uint64_t* func_offset);
+ template <typename SymType>
+ bool GetGlobal(Memory* elf_memory, const std::string& name, uint64_t* memory_address);
+
void ClearCache() {
symbols_.clear();
cur_offset_ = offset_;
diff --git a/libunwindstack/Unwinder.cpp b/libunwindstack/Unwinder.cpp
index a83f85b..d711772 100644
--- a/libunwindstack/Unwinder.cpp
+++ b/libunwindstack/Unwinder.cpp
@@ -27,18 +27,20 @@
#include <android-base/stringprintf.h>
#include <unwindstack/Elf.h>
+#include <unwindstack/JitDebug.h>
#include <unwindstack/MapInfo.h>
#include <unwindstack/Unwinder.h>
namespace unwindstack {
-void Unwinder::FillInFrame(MapInfo* map_info, Elf* elf, uint64_t adjusted_rel_pc) {
+void Unwinder::FillInFrame(MapInfo* map_info, Elf* elf, uint64_t adjusted_rel_pc, uint64_t func_pc) {
size_t frame_num = frames_.size();
frames_.resize(frame_num + 1);
FrameData* frame = &frames_.at(frame_num);
frame->num = frame_num;
frame->sp = regs_->sp();
frame->rel_pc = adjusted_rel_pc;
+ frame->dex_pc = regs_->dex_pc();
if (map_info == nullptr) {
frame->pc = regs_->pc();
@@ -53,7 +55,7 @@
frame->map_flags = map_info->flags;
frame->map_load_bias = elf->GetLoadBias();
- if (!elf->GetFunctionName(frame->rel_pc, &frame->function_name, &frame->function_offset)) {
+ if (!elf->GetFunctionName(func_pc, &frame->function_name, &frame->function_offset)) {
frame->function_name = "";
frame->function_offset = 0;
}
@@ -79,17 +81,20 @@
bool return_address_attempt = false;
bool adjust_pc = false;
+ std::unique_ptr<JitDebug> jit_debug;
for (; frames_.size() < max_frames_;) {
uint64_t cur_pc = regs_->pc();
uint64_t cur_sp = regs_->sp();
MapInfo* map_info = maps_->Find(regs_->pc());
uint64_t rel_pc;
+ uint64_t adjusted_pc;
uint64_t adjusted_rel_pc;
Elf* elf;
if (map_info == nullptr) {
rel_pc = regs_->pc();
adjusted_rel_pc = rel_pc;
+ adjusted_pc = rel_pc;
} else {
if (ShouldStop(map_suffixes_to_ignore, map_info->name)) {
break;
@@ -97,16 +102,30 @@
elf = map_info->GetElf(process_memory_, true);
rel_pc = elf->GetRelPc(regs_->pc(), map_info);
if (adjust_pc) {
- adjusted_rel_pc = regs_->GetAdjustedPc(rel_pc, elf);
+ adjusted_pc = regs_->GetAdjustedPc(rel_pc, elf);
} else {
- adjusted_rel_pc = rel_pc;
+ adjusted_pc = rel_pc;
+ }
+ adjusted_rel_pc = adjusted_pc;
+
+ // If the pc is in an invalid elf file, try and get an Elf object
+ // using the jit debug information.
+ if (!elf->valid() && jit_debug_ != nullptr) {
+ uint64_t adjusted_jit_pc = regs_->pc() - (rel_pc - adjusted_pc);
+ Elf* jit_elf = jit_debug_->GetElf(maps_, adjusted_jit_pc);
+ if (jit_elf != nullptr) {
+ // The jit debug information requires a non relative adjusted pc.
+ adjusted_pc = adjusted_jit_pc;
+ adjusted_rel_pc = adjusted_pc - map_info->start;
+ elf = jit_elf;
+ }
}
}
if (map_info == nullptr || initial_map_names_to_skip == nullptr ||
std::find(initial_map_names_to_skip->begin(), initial_map_names_to_skip->end(),
basename(map_info->name.c_str())) == initial_map_names_to_skip->end()) {
- FillInFrame(map_info, elf, adjusted_rel_pc);
+ FillInFrame(map_info, elf, adjusted_rel_pc, adjusted_pc);
// Once a frame is added, stop skipping frames.
initial_map_names_to_skip = nullptr;
@@ -134,7 +153,7 @@
in_device_map = true;
} else {
bool finished;
- stepped = elf->Step(rel_pc, adjusted_rel_pc, map_info->elf_offset, regs_,
+ stepped = elf->Step(rel_pc, adjusted_pc, map_info->elf_offset, regs_,
process_memory_.get(), &finished);
if (stepped && finished) {
break;
@@ -174,13 +193,13 @@
if (frame_num >= frames_.size()) {
return "";
}
- return FormatFrame(frames_[frame_num], regs_->Format32Bit());
+ return FormatFrame(frames_[frame_num], regs_->Is32Bit());
}
-std::string Unwinder::FormatFrame(const FrameData& frame, bool bits32) {
+std::string Unwinder::FormatFrame(const FrameData& frame, bool is32bit) {
std::string data;
- if (bits32) {
+ if (is32bit) {
data += android::base::StringPrintf(" #%02zu pc %08" PRIx64, frame.num, frame.rel_pc);
} else {
data += android::base::StringPrintf(" #%02zu pc %016" PRIx64, frame.num, frame.rel_pc);
@@ -208,4 +227,9 @@
return data;
}
+void Unwinder::SetJitDebug(JitDebug* jit_debug, ArchEnum arch) {
+ jit_debug->SetArch(arch);
+ jit_debug_ = jit_debug;
+}
+
} // namespace unwindstack
diff --git a/libunwindstack/include/unwindstack/DwarfLocation.h b/libunwindstack/include/unwindstack/DwarfLocation.h
index 3467e6a..0881182 100644
--- a/libunwindstack/include/unwindstack/DwarfLocation.h
+++ b/libunwindstack/include/unwindstack/DwarfLocation.h
@@ -38,7 +38,7 @@
uint64_t values[2];
};
-typedef std::unordered_map<uint16_t, DwarfLocation> dwarf_loc_regs_t;
+typedef std::unordered_map<uint32_t, DwarfLocation> dwarf_loc_regs_t;
} // namespace unwindstack
diff --git a/libunwindstack/include/unwindstack/DwarfSection.h b/libunwindstack/include/unwindstack/DwarfSection.h
index 10be6b4..e0004aa 100644
--- a/libunwindstack/include/unwindstack/DwarfSection.h
+++ b/libunwindstack/include/unwindstack/DwarfSection.h
@@ -132,6 +132,8 @@
const DwarfFde* GetFdeFromIndex(size_t index) override;
+ bool EvalRegister(const DwarfLocation* loc, uint32_t reg, AddressType* reg_ptr, void* info);
+
bool Eval(const DwarfCie* cie, Memory* regular_memory, const dwarf_loc_regs_t& loc_regs,
Regs* regs, bool* finished) override;
diff --git a/libunwindstack/include/unwindstack/Elf.h b/libunwindstack/include/unwindstack/Elf.h
index a85e5f4..5f34391 100644
--- a/libunwindstack/include/unwindstack/Elf.h
+++ b/libunwindstack/include/unwindstack/Elf.h
@@ -59,6 +59,8 @@
bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset);
+ bool GetGlobalVariable(const std::string& name, uint64_t* memory_address);
+
uint64_t GetRelPc(uint64_t pc, const MapInfo* map_info);
bool Step(uint64_t rel_pc, uint64_t adjusted_rel_pc, uint64_t elf_offset, Regs* regs,
@@ -68,6 +70,8 @@
uint64_t GetLoadBias() { return load_bias_; }
+ bool IsValidPc(uint64_t pc);
+
bool valid() { return valid_; }
uint32_t machine_type() { return machine_type_; }
diff --git a/libunwindstack/include/unwindstack/ElfInterface.h b/libunwindstack/include/unwindstack/ElfInterface.h
index 5d3cd5e..faa61ee 100644
--- a/libunwindstack/include/unwindstack/ElfInterface.h
+++ b/libunwindstack/include/unwindstack/ElfInterface.h
@@ -60,9 +60,13 @@
virtual bool GetFunctionName(uint64_t addr, uint64_t load_bias, std::string* name,
uint64_t* offset) = 0;
+ virtual bool GetGlobalVariable(const std::string& name, uint64_t* memory_address) = 0;
+
virtual bool Step(uint64_t rel_pc, uint64_t load_bias, Regs* regs, Memory* process_memory,
bool* finished);
+ virtual bool IsValidPc(uint64_t pc);
+
Memory* CreateGnuDebugdataMemory();
Memory* memory() { return memory_; }
@@ -72,6 +76,7 @@
void SetGnuDebugdataInterface(ElfInterface* interface) { gnu_debugdata_interface_ = interface; }
uint64_t dynamic_offset() { return dynamic_offset_; }
+ uint64_t dynamic_vaddr() { return dynamic_vaddr_; }
uint64_t dynamic_size() { return dynamic_size_; }
uint64_t eh_frame_hdr_offset() { return eh_frame_hdr_offset_; }
uint64_t eh_frame_hdr_size() { return eh_frame_hdr_size_; }
@@ -108,6 +113,9 @@
bool GetFunctionNameWithTemplate(uint64_t addr, uint64_t load_bias, std::string* name,
uint64_t* func_offset);
+ template <typename SymType>
+ bool GetGlobalVariableWithTemplate(const std::string& name, uint64_t* memory_address);
+
virtual bool HandleType(uint64_t, uint32_t, uint64_t) { return false; }
template <typename EhdrType>
@@ -118,6 +126,7 @@
// Stored elf data.
uint64_t dynamic_offset_ = 0;
+ uint64_t dynamic_vaddr_ = 0;
uint64_t dynamic_size_ = 0;
uint64_t eh_frame_hdr_offset_ = 0;
@@ -163,6 +172,10 @@
return ElfInterface::GetFunctionNameWithTemplate<Elf32_Sym>(addr, load_bias, name, func_offset);
}
+ bool GetGlobalVariable(const std::string& name, uint64_t* memory_address) override {
+ return ElfInterface::GetGlobalVariableWithTemplate<Elf32_Sym>(name, memory_address);
+ }
+
static void GetMaxSize(Memory* memory, uint64_t* size) {
GetMaxSizeWithTemplate<Elf32_Ehdr>(memory, size);
}
@@ -188,6 +201,10 @@
return ElfInterface::GetFunctionNameWithTemplate<Elf64_Sym>(addr, load_bias, name, func_offset);
}
+ bool GetGlobalVariable(const std::string& name, uint64_t* memory_address) override {
+ return ElfInterface::GetGlobalVariableWithTemplate<Elf64_Sym>(name, memory_address);
+ }
+
static void GetMaxSize(Memory* memory, uint64_t* size) {
GetMaxSizeWithTemplate<Elf64_Ehdr>(memory, size);
}
diff --git a/libunwindstack/include/unwindstack/JitDebug.h b/libunwindstack/include/unwindstack/JitDebug.h
new file mode 100644
index 0000000..0bcd0b0
--- /dev/null
+++ b/libunwindstack/include/unwindstack/JitDebug.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2017 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 _LIBUNWINDSTACK_JIT_DEBUG_H
+#define _LIBUNWINDSTACK_JIT_DEBUG_H
+
+#include <stdint.h>
+
+#include <memory>
+#include <mutex>
+#include <string>
+#include <vector>
+
+namespace unwindstack {
+
+// Forward declarations.
+class Elf;
+class Maps;
+class Memory;
+enum ArchEnum : uint8_t;
+
+class JitDebug {
+ public:
+ explicit JitDebug(std::shared_ptr<Memory>& memory);
+ JitDebug(std::shared_ptr<Memory>& memory, std::vector<std::string>& search_libs);
+ ~JitDebug();
+
+ Elf* GetElf(Maps* maps, uint64_t pc);
+
+ void SetArch(ArchEnum arch);
+
+ private:
+ void Init(Maps* maps);
+
+ std::shared_ptr<Memory> memory_;
+ uint64_t entry_addr_ = 0;
+ bool initialized_ = false;
+ std::vector<Elf*> elf_list_;
+ std::vector<std::string> search_libs_;
+
+ std::mutex lock_;
+
+ uint64_t (JitDebug::*read_descriptor_func_)(uint64_t) = nullptr;
+ uint64_t (JitDebug::*read_entry_func_)(uint64_t*, uint64_t*) = nullptr;
+
+ uint64_t ReadDescriptor32(uint64_t);
+ uint64_t ReadDescriptor64(uint64_t);
+
+ uint64_t ReadEntry32Pack(uint64_t* start, uint64_t* size);
+ uint64_t ReadEntry32Pad(uint64_t* start, uint64_t* size);
+ uint64_t ReadEntry64(uint64_t* start, uint64_t* size);
+};
+
+} // namespace unwindstack
+
+#endif // _LIBUNWINDSTACK_JIT_DEBUG_H
diff --git a/libunwindstack/include/unwindstack/Memory.h b/libunwindstack/include/unwindstack/Memory.h
index 94ceaab..19bce04 100644
--- a/libunwindstack/include/unwindstack/Memory.h
+++ b/libunwindstack/include/unwindstack/Memory.h
@@ -151,6 +151,19 @@
std::unique_ptr<MemoryRange> memory_;
};
+class MemoryOfflineParts : public Memory {
+ public:
+ MemoryOfflineParts() = default;
+ virtual ~MemoryOfflineParts();
+
+ void Add(MemoryOffline* memory) { memories_.push_back(memory); }
+
+ size_t Read(uint64_t addr, void* dst, size_t size) override;
+
+ private:
+ std::vector<MemoryOffline*> memories_;
+};
+
} // namespace unwindstack
#endif // _LIBUNWINDSTACK_MEMORY_H
diff --git a/libunwindstack/include/unwindstack/Regs.h b/libunwindstack/include/unwindstack/Regs.h
index 613682f..a5ba7a0 100644
--- a/libunwindstack/include/unwindstack/Regs.h
+++ b/libunwindstack/include/unwindstack/Regs.h
@@ -51,12 +51,15 @@
virtual ArchEnum Arch() = 0;
- virtual bool Format32Bit() = 0;
+ virtual bool Is32Bit() = 0;
virtual void* RawData() = 0;
virtual uint64_t pc() = 0;
virtual uint64_t sp() = 0;
+ uint64_t dex_pc() { return dex_pc_; }
+ void set_dex_pc(uint64_t dex_pc) { dex_pc_ = dex_pc; }
+
virtual uint64_t GetAdjustedPc(uint64_t rel_pc, Elf* elf) = 0;
virtual bool StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) = 0;
@@ -79,6 +82,7 @@
uint16_t total_regs_;
uint16_t sp_reg_;
Location return_loc_;
+ uint64_t dex_pc_ = 0;
};
template <typename AddressType>
@@ -94,7 +98,7 @@
void set_pc(AddressType pc) { pc_ = pc; }
void set_sp(AddressType sp) { sp_ = sp; }
- bool Format32Bit() override { return sizeof(AddressType) == sizeof(uint32_t); }
+ bool Is32Bit() override { return sizeof(AddressType) == sizeof(uint32_t); }
inline AddressType& operator[](size_t reg) { return regs_[reg]; }
diff --git a/libunwindstack/include/unwindstack/Unwinder.h b/libunwindstack/include/unwindstack/Unwinder.h
index b64d460..32858ae 100644
--- a/libunwindstack/include/unwindstack/Unwinder.h
+++ b/libunwindstack/include/unwindstack/Unwinder.h
@@ -32,6 +32,8 @@
// Forward declarations.
class Elf;
+class JitDebug;
+enum ArchEnum : uint8_t;
struct FrameData {
size_t num;
@@ -39,6 +41,7 @@
uint64_t rel_pc;
uint64_t pc;
uint64_t sp;
+ uint64_t dex_pc;
std::string function_name;
uint64_t function_offset;
@@ -67,16 +70,19 @@
const std::vector<FrameData>& frames() { return frames_; }
std::string FormatFrame(size_t frame_num);
- static std::string FormatFrame(const FrameData& frame, bool bits32);
+ static std::string FormatFrame(const FrameData& frame, bool is32bit);
+
+ void SetJitDebug(JitDebug* jit_debug, ArchEnum arch);
private:
- void FillInFrame(MapInfo* map_info, Elf* elf, uint64_t adjusted_rel_pc);
+ void FillInFrame(MapInfo* map_info, Elf* elf, uint64_t adjusted_rel_pc, uint64_t adjusted_pc);
size_t max_frames_;
Maps* maps_;
Regs* regs_;
std::vector<FrameData> frames_;
std::shared_ptr<Memory> process_memory_;
+ JitDebug* jit_debug_ = nullptr;
};
} // namespace unwindstack
diff --git a/libunwindstack/tests/DwarfSectionImplTest.cpp b/libunwindstack/tests/DwarfSectionImplTest.cpp
index d54b0bf..dfd2ce0 100644
--- a/libunwindstack/tests/DwarfSectionImplTest.cpp
+++ b/libunwindstack/tests/DwarfSectionImplTest.cpp
@@ -266,13 +266,67 @@
regs.set_pc(0x100);
regs.set_sp(0x2000);
+ regs[1] = 0x100;
+ regs[3] = 0x300;
regs[8] = 0x10;
loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
- loc_regs[1] = DwarfLocation{DWARF_LOCATION_REGISTER, {3, 0}};
- loc_regs[9] = DwarfLocation{DWARF_LOCATION_REGISTER, {1, 0}};
+ loc_regs[1] = DwarfLocation{DWARF_LOCATION_REGISTER, {3, 1}};
+ loc_regs[9] = DwarfLocation{DWARF_LOCATION_REGISTER, {1, 2}};
bool finished;
- ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s, &finished));
- EXPECT_EQ(DWARF_ERROR_ILLEGAL_STATE, this->section_->last_error());
+ ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s, &finished));
+ EXPECT_EQ(0x301U, regs[1]);
+ EXPECT_EQ(0x300U, regs[3]);
+ EXPECT_EQ(0x10U, regs[8]);
+ EXPECT_EQ(0x102U, regs[9]);
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_register_reference_chain) {
+ DwarfCie cie{.return_address_register = 5};
+ RegsImplFake<TypeParam> regs(10, 9);
+ dwarf_loc_regs_t loc_regs;
+
+ regs.set_pc(0x100);
+ regs.set_sp(0x2000);
+ regs[0] = 0x10;
+ regs[1] = 0x20;
+ regs[2] = 0x30;
+ regs[3] = 0x40;
+ regs[4] = 0x50;
+ regs[5] = 0x60;
+ regs[8] = 0x20;
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
+ loc_regs[1] = DwarfLocation{DWARF_LOCATION_REGISTER, {0, 1}};
+ loc_regs[2] = DwarfLocation{DWARF_LOCATION_REGISTER, {1, 2}};
+ loc_regs[3] = DwarfLocation{DWARF_LOCATION_REGISTER, {2, 3}};
+ loc_regs[4] = DwarfLocation{DWARF_LOCATION_REGISTER, {3, 4}};
+ loc_regs[5] = DwarfLocation{DWARF_LOCATION_REGISTER, {4, 5}};
+ bool finished;
+ ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s, &finished));
+ EXPECT_EQ(0x10U, regs[0]);
+ EXPECT_EQ(0x11U, regs[1]);
+ EXPECT_EQ(0x22U, regs[2]);
+ EXPECT_EQ(0x33U, regs[3]);
+ EXPECT_EQ(0x44U, regs[4]);
+ EXPECT_EQ(0x55U, regs[5]);
+ EXPECT_EQ(0x20U, regs[8]);
+}
+
+TYPED_TEST_P(DwarfSectionImplTest, Eval_dex_pc) {
+ DwarfCie cie{.return_address_register = 5};
+ RegsImplFake<TypeParam> regs(10, 9);
+ dwarf_loc_regs_t loc_regs;
+
+ regs.set_pc(0x100);
+ regs.set_sp(0x2000);
+ regs[0] = 0x10;
+ regs[8] = 0x20;
+ loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
+ loc_regs[0x20444558] = DwarfLocation{DWARF_LOCATION_REGISTER, {0, 1}};
+ bool finished;
+ ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s, &finished));
+ EXPECT_EQ(0x10U, regs[0]);
+ EXPECT_EQ(0x20U, regs[8]);
+ EXPECT_EQ(0x11U, regs.dex_pc());
}
TYPED_TEST_P(DwarfSectionImplTest, Eval_invalid_register) {
@@ -840,11 +894,11 @@
DwarfSectionImplTest, Eval_cfa_expr_eval_fail, Eval_cfa_expr_no_stack,
Eval_cfa_expr_is_register, Eval_cfa_expr, Eval_cfa_val_expr, Eval_bad_regs, Eval_no_cfa,
Eval_cfa_bad, Eval_cfa_register_prev, Eval_cfa_register_from_value, Eval_double_indirection,
- Eval_invalid_register, Eval_different_reg_locations, Eval_return_address_undefined,
- Eval_pc_zero, Eval_return_address, Eval_ignore_large_reg_loc, Eval_reg_expr, Eval_reg_val_expr,
- GetCie_fail_should_not_cache, GetCie_32_version_check, GetCie_negative_data_alignment_factor,
- GetCie_64_no_augment, GetCie_augment, GetCie_version_3, GetCie_version_4,
- GetFdeFromOffset_fail_should_not_cache, GetFdeFromOffset_32_no_augment,
+ Eval_register_reference_chain, Eval_dex_pc, Eval_invalid_register, Eval_different_reg_locations,
+ Eval_return_address_undefined, Eval_pc_zero, Eval_return_address, Eval_ignore_large_reg_loc,
+ Eval_reg_expr, Eval_reg_val_expr, GetCie_fail_should_not_cache, GetCie_32_version_check,
+ GetCie_negative_data_alignment_factor, GetCie_64_no_augment, GetCie_augment, GetCie_version_3,
+ GetCie_version_4, GetFdeFromOffset_fail_should_not_cache, GetFdeFromOffset_32_no_augment,
GetFdeFromOffset_32_no_augment_non_zero_segment_size, GetFdeFromOffset_32_augment,
GetFdeFromOffset_64_no_augment, GetFdeFromOffset_cached, GetCfaLocationInfo_cie_not_cached,
GetCfaLocationInfo_cie_cached, Log);
diff --git a/libunwindstack/tests/ElfFake.cpp b/libunwindstack/tests/ElfFake.cpp
index 68de797..ae9da5e 100644
--- a/libunwindstack/tests/ElfFake.cpp
+++ b/libunwindstack/tests/ElfFake.cpp
@@ -43,6 +43,15 @@
return true;
}
+bool ElfInterfaceFake::GetGlobalVariable(const std::string& global, uint64_t* offset) {
+ auto entry = globals_.find(global);
+ if (entry == globals_.end()) {
+ return false;
+ }
+ *offset = entry->second;
+ return true;
+}
+
bool ElfInterfaceFake::Step(uint64_t, uint64_t, Regs* regs, Memory*, bool* finished) {
if (steps_.empty()) {
return false;
diff --git a/libunwindstack/tests/ElfFake.h b/libunwindstack/tests/ElfFake.h
index abf9927..099026c 100644
--- a/libunwindstack/tests/ElfFake.h
+++ b/libunwindstack/tests/ElfFake.h
@@ -21,6 +21,7 @@
#include <deque>
#include <string>
+#include <unordered_map>
#include <unwindstack/Elf.h>
#include <unwindstack/ElfInterface.h>
@@ -55,6 +56,9 @@
void FakeSetLoadBias(uint64_t load_bias) { load_bias_ = load_bias; }
void FakeSetInterface(ElfInterface* interface) { interface_.reset(interface); }
+ void FakeSetGnuDebugdataInterface(ElfInterface* interface) {
+ gnu_debugdata_interface_.reset(interface);
+ }
};
class ElfInterfaceFake : public ElfInterface {
@@ -67,9 +71,14 @@
bool GetSoname(std::string*) override { return false; }
bool GetFunctionName(uint64_t, uint64_t, std::string*, uint64_t*) override;
+ bool GetGlobalVariable(const std::string&, uint64_t*) override;
bool Step(uint64_t, uint64_t, Regs*, Memory*, bool*) override;
+ void FakeSetGlobalVariable(const std::string& global, uint64_t offset) {
+ globals_[global] = offset;
+ }
+
static void FakePushFunctionData(const FunctionData data) { functions_.push_back(data); }
static void FakePushStepData(const StepData data) { steps_.push_back(data); }
@@ -79,6 +88,8 @@
}
private:
+ std::unordered_map<std::string, uint64_t> globals_;
+
static std::deque<FunctionData> functions_;
static std::deque<StepData> steps_;
};
diff --git a/libunwindstack/tests/ElfInterfaceTest.cpp b/libunwindstack/tests/ElfInterfaceTest.cpp
index e138c3a..042c5fb 100644
--- a/libunwindstack/tests/ElfInterfaceTest.cpp
+++ b/libunwindstack/tests/ElfInterfaceTest.cpp
@@ -958,4 +958,189 @@
InitSectionHeadersOffsets<Elf64_Ehdr, Elf64_Shdr, ElfInterface64>();
}
+TEST_F(ElfInterfaceTest, is_valid_pc_from_pt_load) {
+ std::unique_ptr<ElfInterface> elf(new ElfInterface32(&memory_));
+
+ Elf32_Ehdr ehdr;
+ memset(&ehdr, 0, sizeof(ehdr));
+ ehdr.e_phoff = 0x100;
+ ehdr.e_phnum = 1;
+ ehdr.e_phentsize = sizeof(Elf32_Phdr);
+ memory_.SetMemory(0, &ehdr, sizeof(ehdr));
+
+ Elf32_Phdr phdr;
+ memset(&phdr, 0, sizeof(phdr));
+ phdr.p_type = PT_LOAD;
+ phdr.p_vaddr = 0;
+ phdr.p_memsz = 0x10000;
+ phdr.p_flags = PF_R | PF_X;
+ phdr.p_align = 0x1000;
+ memory_.SetMemory(0x100, &phdr, sizeof(phdr));
+
+ uint64_t load_bias = 0;
+ ASSERT_TRUE(elf->Init(&load_bias));
+ EXPECT_EQ(0U, load_bias);
+ EXPECT_TRUE(elf->IsValidPc(0));
+ EXPECT_TRUE(elf->IsValidPc(0x5000));
+ EXPECT_TRUE(elf->IsValidPc(0xffff));
+ EXPECT_FALSE(elf->IsValidPc(0x10000));
+}
+
+TEST_F(ElfInterfaceTest, is_valid_pc_from_pt_load_non_zero_load_bias) {
+ std::unique_ptr<ElfInterface> elf(new ElfInterface32(&memory_));
+
+ Elf32_Ehdr ehdr;
+ memset(&ehdr, 0, sizeof(ehdr));
+ ehdr.e_phoff = 0x100;
+ ehdr.e_phnum = 1;
+ ehdr.e_phentsize = sizeof(Elf32_Phdr);
+ memory_.SetMemory(0, &ehdr, sizeof(ehdr));
+
+ Elf32_Phdr phdr;
+ memset(&phdr, 0, sizeof(phdr));
+ phdr.p_type = PT_LOAD;
+ phdr.p_vaddr = 0x2000;
+ phdr.p_memsz = 0x10000;
+ phdr.p_flags = PF_R | PF_X;
+ phdr.p_align = 0x1000;
+ memory_.SetMemory(0x100, &phdr, sizeof(phdr));
+
+ uint64_t load_bias = 0;
+ ASSERT_TRUE(elf->Init(&load_bias));
+ EXPECT_EQ(0x2000U, load_bias);
+ EXPECT_FALSE(elf->IsValidPc(0));
+ EXPECT_FALSE(elf->IsValidPc(0x1000));
+ EXPECT_FALSE(elf->IsValidPc(0x1fff));
+ EXPECT_TRUE(elf->IsValidPc(0x2000));
+ EXPECT_TRUE(elf->IsValidPc(0x5000));
+ EXPECT_TRUE(elf->IsValidPc(0x11fff));
+ EXPECT_FALSE(elf->IsValidPc(0x12000));
+}
+
+TEST_F(ElfInterfaceTest, is_valid_pc_from_debug_frame) {
+ std::unique_ptr<ElfInterface> elf(new ElfInterface32(&memory_));
+
+ uint64_t sh_offset = 0x100;
+
+ Elf32_Ehdr ehdr;
+ memset(&ehdr, 0, sizeof(ehdr));
+ ehdr.e_shstrndx = 1;
+ ehdr.e_shoff = sh_offset;
+ ehdr.e_shentsize = sizeof(Elf32_Shdr);
+ ehdr.e_shnum = 3;
+ memory_.SetMemory(0, &ehdr, sizeof(ehdr));
+
+ Elf32_Shdr shdr;
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_NULL;
+ memory_.SetMemory(sh_offset, &shdr, sizeof(shdr));
+
+ sh_offset += sizeof(shdr);
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_STRTAB;
+ shdr.sh_name = 1;
+ shdr.sh_offset = 0x500;
+ shdr.sh_size = 0x100;
+ memory_.SetMemory(sh_offset, &shdr, sizeof(shdr));
+ memory_.SetMemory(0x500, ".debug_frame");
+
+ sh_offset += sizeof(shdr);
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_PROGBITS;
+ shdr.sh_name = 0;
+ shdr.sh_addr = 0x600;
+ shdr.sh_offset = 0x600;
+ shdr.sh_size = 0x200;
+ memory_.SetMemory(sh_offset, &shdr, sizeof(shdr));
+
+ // CIE 32.
+ memory_.SetData32(0x600, 0xfc);
+ memory_.SetData32(0x604, 0xffffffff);
+ memory_.SetData8(0x608, 1);
+ memory_.SetData8(0x609, '\0');
+ memory_.SetData8(0x60a, 0x4);
+ memory_.SetData8(0x60b, 0x4);
+ memory_.SetData8(0x60c, 0x1);
+
+ // FDE 32.
+ memory_.SetData32(0x700, 0xfc);
+ memory_.SetData32(0x704, 0);
+ memory_.SetData32(0x708, 0x2100);
+ memory_.SetData32(0x70c, 0x200);
+
+ uint64_t load_bias = 0;
+ ASSERT_TRUE(elf->Init(&load_bias));
+ elf->InitHeaders();
+ EXPECT_EQ(0U, load_bias);
+ EXPECT_FALSE(elf->IsValidPc(0));
+ EXPECT_FALSE(elf->IsValidPc(0x20ff));
+ EXPECT_TRUE(elf->IsValidPc(0x2100));
+ EXPECT_TRUE(elf->IsValidPc(0x2200));
+ EXPECT_TRUE(elf->IsValidPc(0x22ff));
+ EXPECT_FALSE(elf->IsValidPc(0x2300));
+}
+
+TEST_F(ElfInterfaceTest, is_valid_pc_from_eh_frame) {
+ std::unique_ptr<ElfInterface> elf(new ElfInterface32(&memory_));
+
+ uint64_t sh_offset = 0x100;
+
+ Elf32_Ehdr ehdr;
+ memset(&ehdr, 0, sizeof(ehdr));
+ ehdr.e_shstrndx = 1;
+ ehdr.e_shoff = sh_offset;
+ ehdr.e_shentsize = sizeof(Elf32_Shdr);
+ ehdr.e_shnum = 3;
+ memory_.SetMemory(0, &ehdr, sizeof(ehdr));
+
+ Elf32_Shdr shdr;
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_NULL;
+ memory_.SetMemory(sh_offset, &shdr, sizeof(shdr));
+
+ sh_offset += sizeof(shdr);
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_STRTAB;
+ shdr.sh_name = 1;
+ shdr.sh_offset = 0x500;
+ shdr.sh_size = 0x100;
+ memory_.SetMemory(sh_offset, &shdr, sizeof(shdr));
+ memory_.SetMemory(0x500, ".eh_frame");
+
+ sh_offset += sizeof(shdr);
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_PROGBITS;
+ shdr.sh_name = 0;
+ shdr.sh_addr = 0x600;
+ shdr.sh_offset = 0x600;
+ shdr.sh_size = 0x200;
+ memory_.SetMemory(sh_offset, &shdr, sizeof(shdr));
+
+ // CIE 32.
+ memory_.SetData32(0x600, 0xfc);
+ memory_.SetData32(0x604, 0);
+ memory_.SetData8(0x608, 1);
+ memory_.SetData8(0x609, '\0');
+ memory_.SetData8(0x60a, 0x4);
+ memory_.SetData8(0x60b, 0x4);
+ memory_.SetData8(0x60c, 0x1);
+
+ // FDE 32.
+ memory_.SetData32(0x700, 0xfc);
+ memory_.SetData32(0x704, 0x104);
+ memory_.SetData32(0x708, 0x20f8);
+ memory_.SetData32(0x70c, 0x200);
+
+ uint64_t load_bias = 0;
+ ASSERT_TRUE(elf->Init(&load_bias));
+ elf->InitHeaders();
+ EXPECT_EQ(0U, load_bias);
+ EXPECT_FALSE(elf->IsValidPc(0));
+ EXPECT_FALSE(elf->IsValidPc(0x27ff));
+ EXPECT_TRUE(elf->IsValidPc(0x2800));
+ EXPECT_TRUE(elf->IsValidPc(0x2900));
+ EXPECT_TRUE(elf->IsValidPc(0x29ff));
+ EXPECT_FALSE(elf->IsValidPc(0x2a00));
+}
+
} // namespace unwindstack
diff --git a/libunwindstack/tests/ElfTest.cpp b/libunwindstack/tests/ElfTest.cpp
index 5e808ef..7e6a62a 100644
--- a/libunwindstack/tests/ElfTest.cpp
+++ b/libunwindstack/tests/ElfTest.cpp
@@ -346,7 +346,14 @@
void InitHeaders() override {}
bool GetSoname(std::string*) override { return false; }
bool GetFunctionName(uint64_t, uint64_t, std::string*, uint64_t*) override { return false; }
+
MOCK_METHOD5(Step, bool(uint64_t, uint64_t, Regs*, Memory*, bool*));
+ MOCK_METHOD2(GetGlobalVariable, bool(const std::string&, uint64_t*));
+ MOCK_METHOD1(IsValidPc, bool(uint64_t));
+
+ void MockSetDynamicOffset(uint64_t offset) { dynamic_offset_ = offset; }
+ void MockSetDynamicVaddr(uint64_t vaddr) { dynamic_vaddr_ = vaddr; }
+ void MockSetDynamicSize(uint64_t size) { dynamic_size_ = size; }
};
TEST_F(ElfTest, step_in_interface) {
@@ -378,14 +385,200 @@
elf.FakeSetInterface(interface);
MemoryFake process_memory;
- // Invalid relative pc given load_bias.
bool finished;
- ASSERT_FALSE(elf.Step(0x1004, 0x1000, 0x2000, ®s, &process_memory, &finished));
-
EXPECT_CALL(*interface, Step(0x7300, 0x4000, ®s, &process_memory, &finished))
.WillOnce(::testing::Return(true));
ASSERT_TRUE(elf.Step(0x7304, 0x7300, 0x2000, ®s, &process_memory, &finished));
}
+TEST_F(ElfTest, get_global_invalid_elf) {
+ ElfFake elf(memory_);
+ elf.FakeSetValid(false);
+
+ std::string global("something");
+ uint64_t offset;
+ ASSERT_FALSE(elf.GetGlobalVariable(global, &offset));
+}
+
+TEST_F(ElfTest, get_global_valid_not_in_interface) {
+ ElfFake elf(memory_);
+ elf.FakeSetValid(true);
+ elf.FakeSetLoadBias(0);
+
+ ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
+ elf.FakeSetInterface(interface);
+
+ uint64_t offset;
+ std::string global("something");
+ EXPECT_CALL(*interface, GetGlobalVariable(global, &offset)).WillOnce(::testing::Return(false));
+
+ ASSERT_FALSE(elf.GetGlobalVariable(global, &offset));
+}
+
+TEST_F(ElfTest, get_global_valid_below_load_bias) {
+ ElfFake elf(memory_);
+ elf.FakeSetValid(true);
+ elf.FakeSetLoadBias(0x1000);
+
+ ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
+ elf.FakeSetInterface(interface);
+
+ uint64_t offset;
+ std::string global("something");
+ EXPECT_CALL(*interface, GetGlobalVariable(global, &offset))
+ .WillOnce(::testing::DoAll(::testing::SetArgPointee<1>(0x300), ::testing::Return(true)));
+
+ ASSERT_FALSE(elf.GetGlobalVariable(global, &offset));
+}
+
+TEST_F(ElfTest, get_global_valid_dynamic_zero) {
+ ElfFake elf(memory_);
+ elf.FakeSetValid(true);
+ elf.FakeSetLoadBias(0);
+
+ ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
+ elf.FakeSetInterface(interface);
+
+ ElfInterfaceMock* gnu_interface = new ElfInterfaceMock(memory_);
+ elf.FakeSetGnuDebugdataInterface(gnu_interface);
+
+ uint64_t offset;
+ std::string global("something");
+ EXPECT_CALL(*interface, GetGlobalVariable(global, &offset)).WillOnce(::testing::Return(false));
+
+ EXPECT_CALL(*gnu_interface, GetGlobalVariable(global, &offset))
+ .WillOnce(::testing::DoAll(::testing::SetArgPointee<1>(0x500), ::testing::Return(true)));
+
+ ASSERT_TRUE(elf.GetGlobalVariable(global, &offset));
+ EXPECT_EQ(0x500U, offset);
+}
+
+TEST_F(ElfTest, get_global_valid_in_gnu_debugdata_dynamic_zero) {
+ ElfFake elf(memory_);
+ elf.FakeSetValid(true);
+ elf.FakeSetLoadBias(0);
+
+ ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
+ elf.FakeSetInterface(interface);
+
+ uint64_t offset;
+ std::string global("something");
+ EXPECT_CALL(*interface, GetGlobalVariable(global, &offset))
+ .WillOnce(::testing::DoAll(::testing::SetArgPointee<1>(0x300), ::testing::Return(true)));
+
+ ASSERT_TRUE(elf.GetGlobalVariable(global, &offset));
+ EXPECT_EQ(0x300U, offset);
+}
+
+TEST_F(ElfTest, get_global_valid_dynamic_zero_non_zero_load_bias) {
+ ElfFake elf(memory_);
+ elf.FakeSetValid(true);
+ elf.FakeSetLoadBias(0x100);
+
+ ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
+ elf.FakeSetInterface(interface);
+
+ uint64_t offset;
+ std::string global("something");
+ EXPECT_CALL(*interface, GetGlobalVariable(global, &offset))
+ .WillOnce(::testing::DoAll(::testing::SetArgPointee<1>(0x300), ::testing::Return(true)));
+
+ ASSERT_TRUE(elf.GetGlobalVariable(global, &offset));
+ EXPECT_EQ(0x200U, offset);
+}
+
+TEST_F(ElfTest, get_global_valid_dynamic_adjust_negative) {
+ ElfFake elf(memory_);
+ elf.FakeSetValid(true);
+ elf.FakeSetLoadBias(0);
+
+ ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
+ interface->MockSetDynamicOffset(0x400);
+ interface->MockSetDynamicVaddr(0x800);
+ interface->MockSetDynamicSize(0x100);
+ elf.FakeSetInterface(interface);
+
+ uint64_t offset;
+ std::string global("something");
+ EXPECT_CALL(*interface, GetGlobalVariable(global, &offset))
+ .WillOnce(::testing::DoAll(::testing::SetArgPointee<1>(0x850), ::testing::Return(true)));
+
+ ASSERT_TRUE(elf.GetGlobalVariable(global, &offset));
+ EXPECT_EQ(0x450U, offset);
+}
+
+TEST_F(ElfTest, get_global_valid_dynamic_adjust_positive) {
+ ElfFake elf(memory_);
+ elf.FakeSetValid(true);
+ elf.FakeSetLoadBias(0);
+
+ ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
+ interface->MockSetDynamicOffset(0x1000);
+ interface->MockSetDynamicVaddr(0x800);
+ interface->MockSetDynamicSize(0x100);
+ elf.FakeSetInterface(interface);
+
+ uint64_t offset;
+ std::string global("something");
+ EXPECT_CALL(*interface, GetGlobalVariable(global, &offset))
+ .WillOnce(::testing::DoAll(::testing::SetArgPointee<1>(0x850), ::testing::Return(true)));
+
+ ASSERT_TRUE(elf.GetGlobalVariable(global, &offset));
+ EXPECT_EQ(0x1050U, offset);
+}
+
+TEST_F(ElfTest, is_valid_pc_elf_invalid) {
+ ElfFake elf(memory_);
+ elf.FakeSetValid(false);
+ elf.FakeSetLoadBias(0);
+
+ EXPECT_FALSE(elf.IsValidPc(0x100));
+ EXPECT_FALSE(elf.IsValidPc(0x200));
+}
+
+TEST_F(ElfTest, is_valid_pc_interface) {
+ ElfFake elf(memory_);
+ elf.FakeSetValid(true);
+ elf.FakeSetLoadBias(0);
+
+ ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
+ elf.FakeSetInterface(interface);
+
+ EXPECT_CALL(*interface, IsValidPc(0x1500)).WillOnce(::testing::Return(true));
+
+ EXPECT_TRUE(elf.IsValidPc(0x1500));
+}
+
+TEST_F(ElfTest, is_valid_pc_non_zero_load_bias) {
+ ElfFake elf(memory_);
+ elf.FakeSetValid(true);
+ elf.FakeSetLoadBias(0x1000);
+
+ ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
+ elf.FakeSetInterface(interface);
+
+ EXPECT_CALL(*interface, IsValidPc(0x500)).WillOnce(::testing::Return(true));
+
+ EXPECT_FALSE(elf.IsValidPc(0x100));
+ EXPECT_FALSE(elf.IsValidPc(0x200));
+ EXPECT_TRUE(elf.IsValidPc(0x1500));
+}
+
+TEST_F(ElfTest, is_valid_pc_from_gnu_debugdata) {
+ ElfFake elf(memory_);
+ elf.FakeSetValid(true);
+ elf.FakeSetLoadBias(0);
+
+ ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
+ elf.FakeSetInterface(interface);
+ ElfInterfaceMock* gnu_interface = new ElfInterfaceMock(memory_);
+ elf.FakeSetGnuDebugdataInterface(gnu_interface);
+
+ EXPECT_CALL(*interface, IsValidPc(0x1500)).WillOnce(::testing::Return(false));
+ EXPECT_CALL(*gnu_interface, IsValidPc(0x1500)).WillOnce(::testing::Return(true));
+
+ EXPECT_TRUE(elf.IsValidPc(0x1500));
+}
+
} // namespace unwindstack
diff --git a/libunwindstack/tests/JitDebugTest.cpp b/libunwindstack/tests/JitDebugTest.cpp
new file mode 100644
index 0000000..1a50989
--- /dev/null
+++ b/libunwindstack/tests/JitDebugTest.cpp
@@ -0,0 +1,409 @@
+/*
+ * Copyright (C) 2018 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 <elf.h>
+#include <string.h>
+
+#include <memory>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include <unwindstack/Elf.h>
+#include <unwindstack/JitDebug.h>
+#include <unwindstack/MapInfo.h>
+#include <unwindstack/Maps.h>
+#include <unwindstack/Memory.h>
+
+#include "ElfFake.h"
+#include "MemoryFake.h"
+
+namespace unwindstack {
+
+class JitDebugTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ memory_ = new MemoryFake;
+ process_memory_.reset(memory_);
+
+ jit_debug_.reset(new JitDebug(process_memory_));
+ jit_debug_->SetArch(ARCH_ARM);
+
+ maps_.reset(
+ new BufferMaps("1000-4000 ---s 00000000 00:00 0\n"
+ "4000-6000 r--s 00000000 00:00 0\n"
+ "6000-8000 -w-s 00000000 00:00 0\n"
+ "a000-c000 --xp 00000000 00:00 0\n"
+ "c000-f000 rwxp 00000000 00:00 0\n"
+ "f000-11000 r-xp 00000000 00:00 0\n"
+ "12000-14000 r-xp 00000000 00:00 0\n"
+ "100000-110000 rw-p 0000000 00:00 0\n"
+ "200000-210000 rw-p 0000000 00:00 0\n"));
+ ASSERT_TRUE(maps_->Parse());
+
+ MapInfo* map_info = maps_->Get(3);
+ ASSERT_TRUE(map_info != nullptr);
+ elf_memories_.push_back(new MemoryFake);
+ ElfFake* elf = new ElfFake(elf_memories_.back());
+ elf->FakeSetValid(true);
+ ElfInterfaceFake* interface = new ElfInterfaceFake(elf_memories_.back());
+ elf->FakeSetInterface(interface);
+ interface->FakeSetGlobalVariable("__jit_debug_descriptor", 0x800);
+ map_info->elf = elf;
+
+ map_info = maps_->Get(5);
+ ASSERT_TRUE(map_info != nullptr);
+ elf_memories_.push_back(new MemoryFake);
+ elf = new ElfFake(elf_memories_.back());
+ elf->FakeSetValid(true);
+ interface = new ElfInterfaceFake(elf_memories_.back());
+ elf->FakeSetInterface(interface);
+ interface->FakeSetGlobalVariable("__jit_debug_descriptor", 0x800);
+ map_info->elf = elf;
+
+ map_info = maps_->Get(6);
+ ASSERT_TRUE(map_info != nullptr);
+ elf_memories_.push_back(new MemoryFake);
+ elf = new ElfFake(elf_memories_.back());
+ elf->FakeSetValid(true);
+ interface = new ElfInterfaceFake(elf_memories_.back());
+ elf->FakeSetInterface(interface);
+ interface->FakeSetGlobalVariable("__jit_debug_descriptor", 0x800);
+ map_info->elf = elf;
+ }
+
+ template <typename EhdrType, typename ShdrType>
+ void CreateElf(uint64_t offset, uint8_t class_type, uint8_t machine_type, uint32_t pc,
+ uint32_t size) {
+ EhdrType ehdr;
+ memset(&ehdr, 0, sizeof(ehdr));
+ uint64_t sh_offset = sizeof(ehdr);
+ memcpy(ehdr.e_ident, ELFMAG, SELFMAG);
+ ehdr.e_ident[EI_CLASS] = class_type;
+ ehdr.e_machine = machine_type;
+ ehdr.e_shstrndx = 1;
+ ehdr.e_shoff = sh_offset;
+ ehdr.e_shentsize = sizeof(ShdrType);
+ ehdr.e_shnum = 3;
+ memory_->SetMemory(offset, &ehdr, sizeof(ehdr));
+
+ ShdrType shdr;
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_NULL;
+ memory_->SetMemory(offset + sh_offset, &shdr, sizeof(shdr));
+
+ sh_offset += sizeof(shdr);
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_STRTAB;
+ shdr.sh_name = 1;
+ shdr.sh_offset = 0x500;
+ shdr.sh_size = 0x100;
+ memory_->SetMemory(offset + sh_offset, &shdr, sizeof(shdr));
+ memory_->SetMemory(offset + 0x500, ".debug_frame");
+
+ sh_offset += sizeof(shdr);
+ memset(&shdr, 0, sizeof(shdr));
+ shdr.sh_type = SHT_PROGBITS;
+ shdr.sh_name = 0;
+ shdr.sh_addr = 0x600;
+ shdr.sh_offset = 0x600;
+ shdr.sh_size = 0x200;
+ memory_->SetMemory(offset + sh_offset, &shdr, sizeof(shdr));
+
+ // Now add a single cie/fde.
+ uint64_t dwarf_offset = offset + 0x600;
+ if (class_type == ELFCLASS32) {
+ // CIE 32 information.
+ memory_->SetData32(dwarf_offset, 0xfc);
+ memory_->SetData32(dwarf_offset + 0x4, 0xffffffff);
+ memory_->SetData8(dwarf_offset + 0x8, 1);
+ memory_->SetData8(dwarf_offset + 0x9, '\0');
+ memory_->SetData8(dwarf_offset + 0xa, 0x4);
+ memory_->SetData8(dwarf_offset + 0xb, 0x4);
+ memory_->SetData8(dwarf_offset + 0xc, 0x1);
+
+ // FDE 32 information.
+ memory_->SetData32(dwarf_offset + 0x100, 0xfc);
+ memory_->SetData32(dwarf_offset + 0x104, 0);
+ memory_->SetData32(dwarf_offset + 0x108, pc);
+ memory_->SetData32(dwarf_offset + 0x10c, size);
+ } else {
+ // CIE 64 information.
+ memory_->SetData32(dwarf_offset, 0xffffffff);
+ memory_->SetData64(dwarf_offset + 4, 0xf4);
+ memory_->SetData64(dwarf_offset + 0xc, 0xffffffffffffffffULL);
+ memory_->SetData8(dwarf_offset + 0x14, 1);
+ memory_->SetData8(dwarf_offset + 0x15, '\0');
+ memory_->SetData8(dwarf_offset + 0x16, 0x4);
+ memory_->SetData8(dwarf_offset + 0x17, 0x4);
+ memory_->SetData8(dwarf_offset + 0x18, 0x1);
+
+ // FDE 64 information.
+ memory_->SetData32(dwarf_offset + 0x100, 0xffffffff);
+ memory_->SetData64(dwarf_offset + 0x104, 0xf4);
+ memory_->SetData64(dwarf_offset + 0x10c, 0);
+ memory_->SetData64(dwarf_offset + 0x114, pc);
+ memory_->SetData64(dwarf_offset + 0x11c, size);
+ }
+ }
+
+ void WriteDescriptor32(uint64_t addr, uint32_t entry);
+ void WriteDescriptor64(uint64_t addr, uint64_t entry);
+ void WriteEntry32Pack(uint64_t addr, uint32_t prev, uint32_t next, uint32_t elf_addr,
+ uint64_t elf_size);
+ void WriteEntry32Pad(uint64_t addr, uint32_t prev, uint32_t next, uint32_t elf_addr,
+ uint64_t elf_size);
+ void WriteEntry64(uint64_t addr, uint64_t prev, uint64_t next, uint64_t elf_addr,
+ uint64_t elf_size);
+
+ std::shared_ptr<Memory> process_memory_;
+ MemoryFake* memory_;
+ std::vector<MemoryFake*> elf_memories_;
+ std::unique_ptr<JitDebug> jit_debug_;
+ std::unique_ptr<BufferMaps> maps_;
+};
+
+void JitDebugTest::WriteDescriptor32(uint64_t addr, uint32_t entry) {
+ // Format of the 32 bit JITDescriptor structure:
+ // uint32_t version
+ memory_->SetData32(addr, 1);
+ // uint32_t action_flag
+ memory_->SetData32(addr + 4, 0);
+ // uint32_t relevant_entry
+ memory_->SetData32(addr + 8, 0);
+ // uint32_t first_entry
+ memory_->SetData32(addr + 12, entry);
+}
+
+void JitDebugTest::WriteDescriptor64(uint64_t addr, uint64_t entry) {
+ // Format of the 64 bit JITDescriptor structure:
+ // uint32_t version
+ memory_->SetData32(addr, 1);
+ // uint32_t action_flag
+ memory_->SetData32(addr + 4, 0);
+ // uint64_t relevant_entry
+ memory_->SetData64(addr + 8, 0);
+ // uint64_t first_entry
+ memory_->SetData64(addr + 16, entry);
+}
+
+void JitDebugTest::WriteEntry32Pack(uint64_t addr, uint32_t prev, uint32_t next, uint32_t elf_addr,
+ uint64_t elf_size) {
+ // Format of the 32 bit JITCodeEntry structure:
+ // uint32_t next
+ memory_->SetData32(addr, next);
+ // uint32_t prev
+ memory_->SetData32(addr + 4, prev);
+ // uint32_t symfile_addr
+ memory_->SetData32(addr + 8, elf_addr);
+ // uint64_t symfile_size
+ memory_->SetData64(addr + 12, elf_size);
+}
+
+void JitDebugTest::WriteEntry32Pad(uint64_t addr, uint32_t prev, uint32_t next, uint32_t elf_addr,
+ uint64_t elf_size) {
+ // Format of the 32 bit JITCodeEntry structure:
+ // uint32_t next
+ memory_->SetData32(addr, next);
+ // uint32_t prev
+ memory_->SetData32(addr + 4, prev);
+ // uint32_t symfile_addr
+ memory_->SetData32(addr + 8, elf_addr);
+ // uint32_t pad
+ memory_->SetData32(addr + 12, 0);
+ // uint64_t symfile_size
+ memory_->SetData64(addr + 16, elf_size);
+}
+
+void JitDebugTest::WriteEntry64(uint64_t addr, uint64_t prev, uint64_t next, uint64_t elf_addr,
+ uint64_t elf_size) {
+ // Format of the 64 bit JITCodeEntry structure:
+ // uint64_t next
+ memory_->SetData64(addr, next);
+ // uint64_t prev
+ memory_->SetData64(addr + 8, prev);
+ // uint64_t symfile_addr
+ memory_->SetData64(addr + 16, elf_addr);
+ // uint64_t symfile_size
+ memory_->SetData64(addr + 24, elf_size);
+}
+
+TEST_F(JitDebugTest, get_elf_invalid) {
+ Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500);
+ ASSERT_TRUE(elf == nullptr);
+}
+
+TEST_F(JitDebugTest, get_elf_no_global_variable) {
+ maps_.reset(new BufferMaps(""));
+ Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500);
+ ASSERT_TRUE(elf == nullptr);
+}
+
+TEST_F(JitDebugTest, get_elf_no_valid_descriptor_in_memory) {
+ CreateElf<Elf32_Ehdr, Elf32_Shdr>(0x4000, ELFCLASS32, EM_ARM, 0x1500, 0x200);
+
+ Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500);
+ ASSERT_TRUE(elf == nullptr);
+}
+
+TEST_F(JitDebugTest, get_elf_no_valid_code_entry) {
+ CreateElf<Elf32_Ehdr, Elf32_Shdr>(0x4000, ELFCLASS32, EM_ARM, 0x1500, 0x200);
+
+ WriteDescriptor32(0xf800, 0x200000);
+
+ Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500);
+ ASSERT_TRUE(elf == nullptr);
+}
+
+TEST_F(JitDebugTest, get_elf_invalid_descriptor_first_entry) {
+ CreateElf<Elf32_Ehdr, Elf32_Shdr>(0x4000, ELFCLASS32, EM_ARM, 0x1500, 0x200);
+
+ WriteDescriptor32(0xf800, 0);
+
+ Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500);
+ ASSERT_TRUE(elf == nullptr);
+}
+
+TEST_F(JitDebugTest, get_elf_invalid_descriptor_version) {
+ CreateElf<Elf32_Ehdr, Elf32_Shdr>(0x4000, ELFCLASS32, EM_ARM, 0x1500, 0x200);
+
+ WriteDescriptor32(0xf800, 0x20000);
+ // Set the version to an invalid value.
+ memory_->SetData32(0xf800, 2);
+
+ Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500);
+ ASSERT_TRUE(elf == nullptr);
+}
+
+TEST_F(JitDebugTest, get_elf_32) {
+ CreateElf<Elf32_Ehdr, Elf32_Shdr>(0x4000, ELFCLASS32, EM_ARM, 0x1500, 0x200);
+
+ WriteDescriptor32(0xf800, 0x200000);
+ WriteEntry32Pad(0x200000, 0, 0, 0x4000, 0x1000);
+
+ Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500);
+ ASSERT_TRUE(elf != nullptr);
+
+ // Clear the memory and verify all of the data is cached.
+ memory_->Clear();
+ Elf* elf2 = jit_debug_->GetElf(maps_.get(), 0x1500);
+ ASSERT_TRUE(elf2 != nullptr);
+ EXPECT_EQ(elf, elf2);
+}
+
+TEST_F(JitDebugTest, get_multiple_jit_debug_descriptors_valid) {
+ CreateElf<Elf32_Ehdr, Elf32_Shdr>(0x4000, ELFCLASS32, EM_ARM, 0x1500, 0x200);
+ CreateElf<Elf32_Ehdr, Elf32_Shdr>(0x5000, ELFCLASS32, EM_ARM, 0x2000, 0x300);
+
+ WriteDescriptor32(0xf800, 0x200000);
+ WriteEntry32Pad(0x200000, 0, 0, 0x4000, 0x1000);
+ WriteDescriptor32(0x12800, 0x201000);
+ WriteEntry32Pad(0x201000, 0, 0, 0x5000, 0x1000);
+
+ ASSERT_TRUE(jit_debug_->GetElf(maps_.get(), 0x1500) != nullptr);
+ ASSERT_TRUE(jit_debug_->GetElf(maps_.get(), 0x2000) == nullptr);
+
+ // Now clear the descriptor entry for the first one.
+ WriteDescriptor32(0xf800, 0);
+ jit_debug_.reset(new JitDebug(process_memory_));
+ jit_debug_->SetArch(ARCH_ARM);
+
+ ASSERT_TRUE(jit_debug_->GetElf(maps_.get(), 0x1500) == nullptr);
+ ASSERT_TRUE(jit_debug_->GetElf(maps_.get(), 0x2000) != nullptr);
+}
+
+TEST_F(JitDebugTest, get_elf_x86) {
+ CreateElf<Elf32_Ehdr, Elf32_Shdr>(0x4000, ELFCLASS32, EM_ARM, 0x1500, 0x200);
+
+ WriteDescriptor32(0xf800, 0x200000);
+ WriteEntry32Pack(0x200000, 0, 0, 0x4000, 0x1000);
+
+ jit_debug_->SetArch(ARCH_X86);
+ Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500);
+ ASSERT_TRUE(elf != nullptr);
+
+ // Clear the memory and verify all of the data is cached.
+ memory_->Clear();
+ Elf* elf2 = jit_debug_->GetElf(maps_.get(), 0x1500);
+ ASSERT_TRUE(elf2 != nullptr);
+ EXPECT_EQ(elf, elf2);
+}
+
+TEST_F(JitDebugTest, get_elf_64) {
+ CreateElf<Elf64_Ehdr, Elf64_Shdr>(0x4000, ELFCLASS64, EM_AARCH64, 0x1500, 0x200);
+
+ WriteDescriptor64(0xf800, 0x200000);
+ WriteEntry64(0x200000, 0, 0, 0x4000, 0x1000);
+
+ jit_debug_->SetArch(ARCH_ARM64);
+ Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500);
+ ASSERT_TRUE(elf != nullptr);
+
+ // Clear the memory and verify all of the data is cached.
+ memory_->Clear();
+ Elf* elf2 = jit_debug_->GetElf(maps_.get(), 0x1500);
+ ASSERT_TRUE(elf2 != nullptr);
+ EXPECT_EQ(elf, elf2);
+}
+
+TEST_F(JitDebugTest, get_elf_multiple_entries) {
+ CreateElf<Elf32_Ehdr, Elf32_Shdr>(0x4000, ELFCLASS32, EM_ARM, 0x1500, 0x200);
+ CreateElf<Elf32_Ehdr, Elf32_Shdr>(0x5000, ELFCLASS32, EM_ARM, 0x2300, 0x400);
+
+ WriteDescriptor32(0xf800, 0x200000);
+ WriteEntry32Pad(0x200000, 0, 0x200100, 0x4000, 0x1000);
+ WriteEntry32Pad(0x200100, 0x200100, 0, 0x5000, 0x1000);
+
+ Elf* elf_2 = jit_debug_->GetElf(maps_.get(), 0x2400);
+ ASSERT_TRUE(elf_2 != nullptr);
+
+ Elf* elf_1 = jit_debug_->GetElf(maps_.get(), 0x1600);
+ ASSERT_TRUE(elf_1 != nullptr);
+
+ // Clear the memory and verify all of the data is cached.
+ memory_->Clear();
+ EXPECT_EQ(elf_1, jit_debug_->GetElf(maps_.get(), 0x1500));
+ EXPECT_EQ(elf_1, jit_debug_->GetElf(maps_.get(), 0x16ff));
+ EXPECT_EQ(elf_2, jit_debug_->GetElf(maps_.get(), 0x2300));
+ EXPECT_EQ(elf_2, jit_debug_->GetElf(maps_.get(), 0x26ff));
+ EXPECT_EQ(nullptr, jit_debug_->GetElf(maps_.get(), 0x1700));
+ EXPECT_EQ(nullptr, jit_debug_->GetElf(maps_.get(), 0x2700));
+}
+
+TEST_F(JitDebugTest, get_elf_search_libs) {
+ CreateElf<Elf32_Ehdr, Elf32_Shdr>(0x4000, ELFCLASS32, EM_ARM, 0x1500, 0x200);
+
+ WriteDescriptor32(0xf800, 0x200000);
+ WriteEntry32Pad(0x200000, 0, 0, 0x4000, 0x1000);
+
+ // Only search a given named list of libs.
+ std::vector<std::string> libs{"libart.so"};
+ jit_debug_.reset(new JitDebug(process_memory_, libs));
+ jit_debug_->SetArch(ARCH_ARM);
+ EXPECT_TRUE(jit_debug_->GetElf(maps_.get(), 0x1500) == nullptr);
+
+ // Change the name of the map that includes the value and verify this works.
+ MapInfo* map_info = maps_->Get(5);
+ map_info->name = "/system/lib/libart.so";
+ jit_debug_.reset(new JitDebug(process_memory_, libs));
+ // Make sure that clearing our copy of the libs doesn't affect the
+ // JitDebug object.
+ libs.clear();
+ jit_debug_->SetArch(ARCH_ARM);
+ EXPECT_TRUE(jit_debug_->GetElf(maps_.get(), 0x1500) != nullptr);
+}
+
+} // namespace unwindstack
diff --git a/libunwindstack/tests/RegsFake.h b/libunwindstack/tests/RegsFake.h
index b81b2ca..8f7d913 100644
--- a/libunwindstack/tests/RegsFake.h
+++ b/libunwindstack/tests/RegsFake.h
@@ -45,7 +45,7 @@
void IterateRegisters(std::function<void(const char*, uint64_t)>) override {}
- bool Format32Bit() { return false; }
+ bool Is32Bit() { return false; }
uint64_t GetAdjustedPc(uint64_t rel_pc, Elf*) override { return rel_pc - 2; }
diff --git a/libunwindstack/tests/RegsTest.cpp b/libunwindstack/tests/RegsTest.cpp
index 3f84890..15d5458 100644
--- a/libunwindstack/tests/RegsTest.cpp
+++ b/libunwindstack/tests/RegsTest.cpp
@@ -188,7 +188,7 @@
regs_arm.set_pc(0x1500);
EXPECT_EQ(0x500U, invalid_elf->GetRelPc(regs_arm.pc(), &map_info));
- EXPECT_EQ(0x500U, regs_arm.GetAdjustedPc(0x500U, invalid_elf));
+ EXPECT_EQ(0x4fcU, regs_arm.GetAdjustedPc(0x500U, invalid_elf));
regs_arm64.set_pc(0x1600);
EXPECT_EQ(0x600U, invalid_elf->GetRelPc(regs_arm64.pc(), &map_info));
diff --git a/libunwindstack/tests/SymbolsTest.cpp b/libunwindstack/tests/SymbolsTest.cpp
index da258a6..45a7b58 100644
--- a/libunwindstack/tests/SymbolsTest.cpp
+++ b/libunwindstack/tests/SymbolsTest.cpp
@@ -330,9 +330,69 @@
ASSERT_EQ(3U, func_offset);
}
+TYPED_TEST_P(SymbolsTest, get_global) {
+ uint64_t start_offset = 0x1000;
+ uint64_t str_offset = 0xa000;
+ Symbols symbols(start_offset, 4 * sizeof(TypeParam), sizeof(TypeParam), str_offset, 0x1000);
+
+ TypeParam sym;
+ memset(&sym, 0, sizeof(sym));
+ sym.st_shndx = SHN_COMMON;
+ sym.st_info = STT_OBJECT | (STB_GLOBAL << 4);
+ sym.st_name = 0x100;
+ this->memory_.SetMemory(start_offset, &sym, sizeof(sym));
+ this->memory_.SetMemory(str_offset + 0x100, "global_0");
+
+ start_offset += sizeof(sym);
+ memset(&sym, 0, sizeof(sym));
+ sym.st_shndx = SHN_COMMON;
+ sym.st_info = STT_FUNC;
+ sym.st_name = 0x200;
+ sym.st_value = 0x10000;
+ sym.st_size = 0x100;
+ this->memory_.SetMemory(start_offset, &sym, sizeof(sym));
+ this->memory_.SetMemory(str_offset + 0x200, "function_0");
+
+ start_offset += sizeof(sym);
+ memset(&sym, 0, sizeof(sym));
+ sym.st_shndx = SHN_COMMON;
+ sym.st_info = STT_OBJECT | (STB_GLOBAL << 4);
+ sym.st_name = 0x300;
+ this->memory_.SetMemory(start_offset, &sym, sizeof(sym));
+ this->memory_.SetMemory(str_offset + 0x300, "global_1");
+
+ start_offset += sizeof(sym);
+ memset(&sym, 0, sizeof(sym));
+ sym.st_shndx = SHN_COMMON;
+ sym.st_info = STT_FUNC;
+ sym.st_name = 0x400;
+ sym.st_value = 0x12000;
+ sym.st_size = 0x100;
+ this->memory_.SetMemory(start_offset, &sym, sizeof(sym));
+ this->memory_.SetMemory(str_offset + 0x400, "function_1");
+
+ uint64_t offset;
+ EXPECT_TRUE(symbols.GetGlobal<TypeParam>(&this->memory_, "global_0", &offset));
+ EXPECT_TRUE(symbols.GetGlobal<TypeParam>(&this->memory_, "global_1", &offset));
+ EXPECT_TRUE(symbols.GetGlobal<TypeParam>(&this->memory_, "global_0", &offset));
+ EXPECT_TRUE(symbols.GetGlobal<TypeParam>(&this->memory_, "global_1", &offset));
+
+ EXPECT_FALSE(symbols.GetGlobal<TypeParam>(&this->memory_, "function_0", &offset));
+ EXPECT_FALSE(symbols.GetGlobal<TypeParam>(&this->memory_, "function_1", &offset));
+
+ std::string name;
+ EXPECT_TRUE(symbols.GetName<TypeParam>(0x10002, 0, &this->memory_, &name, &offset));
+ EXPECT_EQ("function_0", name);
+ EXPECT_EQ(2U, offset);
+
+ EXPECT_TRUE(symbols.GetName<TypeParam>(0x12004, 0, &this->memory_, &name, &offset));
+ EXPECT_EQ("function_1", name);
+ EXPECT_EQ(4U, offset);
+}
+
REGISTER_TYPED_TEST_CASE_P(SymbolsTest, function_bounds_check, no_symbol, multiple_entries,
multiple_entries_nonstandard_size, load_bias, symtab_value_out_of_bounds,
- symtab_read_cached);
+ symtab_read_cached, get_global);
typedef ::testing::Types<Elf32_Sym, Elf64_Sym> SymbolsTestTypes;
INSTANTIATE_TYPED_TEST_CASE_P(, SymbolsTest, SymbolsTestTypes);
diff --git a/libunwindstack/tests/UnwindOfflineTest.cpp b/libunwindstack/tests/UnwindOfflineTest.cpp
index 8f28036..582ac18 100644
--- a/libunwindstack/tests/UnwindOfflineTest.cpp
+++ b/libunwindstack/tests/UnwindOfflineTest.cpp
@@ -25,14 +25,17 @@
#include <string>
#include <vector>
+#include <unwindstack/JitDebug.h>
#include <unwindstack/Maps.h>
#include <unwindstack/Memory.h>
#include <unwindstack/RegsArm.h>
#include <unwindstack/RegsArm64.h>
+#include <unwindstack/RegsX86.h>
#include <unwindstack/Unwinder.h>
#include "MachineArm.h"
#include "MachineArm64.h"
+#include "MachineX86.h"
#include "ElfTestUtils.h"
@@ -92,7 +95,7 @@
" #00 pc 0001a9f8 libc.so (abort+63)\n"
" #01 pc 00006a1b libbase.so (_ZN7android4base14DefaultAborterEPKc+6)\n"
" #02 pc 00007441 libbase.so (_ZN7android4base10LogMessageD2Ev+748)\n"
- " #03 pc 00015149 /does/not/exist/libhidlbase.so\n",
+ " #03 pc 00015147 /does/not/exist/libhidlbase.so\n",
frame_info);
}
@@ -200,4 +203,426 @@
frame_info);
}
+static void AddMemory(std::string file_name, MemoryOfflineParts* parts) {
+ MemoryOffline* memory = new MemoryOffline;
+ ASSERT_TRUE(memory->Init(file_name.c_str(), 0));
+ parts->Add(memory);
+}
+
+TEST(UnwindOfflineTest, jit_debug_x86_32) {
+ std::string dir(TestGetFileDirectory() + "offline/jit_debug_x86_32/");
+
+ MemoryOfflineParts* memory = new MemoryOfflineParts;
+ AddMemory(dir + "descriptor.data", memory);
+ AddMemory(dir + "stack.data", memory);
+ for (size_t i = 0; i < 7; i++) {
+ AddMemory(dir + "entry" + std::to_string(i) + ".data", memory);
+ AddMemory(dir + "jit" + std::to_string(i) + ".data", memory);
+ }
+
+ FILE* fp = fopen((dir + "regs.txt").c_str(), "r");
+ ASSERT_TRUE(fp != nullptr);
+ RegsX86 regs;
+ uint64_t reg_value;
+ ASSERT_EQ(1, fscanf(fp, "eax: %" SCNx64 "\n", ®_value));
+ regs[X86_REG_EAX] = reg_value;
+ ASSERT_EQ(1, fscanf(fp, "ebx: %" SCNx64 "\n", ®_value));
+ regs[X86_REG_EBX] = reg_value;
+ ASSERT_EQ(1, fscanf(fp, "ecx: %" SCNx64 "\n", ®_value));
+ regs[X86_REG_ECX] = reg_value;
+ ASSERT_EQ(1, fscanf(fp, "edx: %" SCNx64 "\n", ®_value));
+ regs[X86_REG_EDX] = reg_value;
+ ASSERT_EQ(1, fscanf(fp, "ebp: %" SCNx64 "\n", ®_value));
+ regs[X86_REG_EBP] = reg_value;
+ ASSERT_EQ(1, fscanf(fp, "edi: %" SCNx64 "\n", ®_value));
+ regs[X86_REG_EDI] = reg_value;
+ ASSERT_EQ(1, fscanf(fp, "esi: %" SCNx64 "\n", ®_value));
+ regs[X86_REG_ESI] = reg_value;
+ ASSERT_EQ(1, fscanf(fp, "esp: %" SCNx64 "\n", ®_value));
+ regs[X86_REG_ESP] = reg_value;
+ ASSERT_EQ(1, fscanf(fp, "eip: %" SCNx64 "\n", ®_value));
+ regs[X86_REG_EIP] = reg_value;
+ regs.SetFromRaw();
+ fclose(fp);
+
+ fp = fopen((dir + "maps.txt").c_str(), "r");
+ ASSERT_TRUE(fp != nullptr);
+ // The file is guaranteed to be less than 4096 bytes.
+ std::vector<char> buffer(4096);
+ ASSERT_NE(0U, fread(buffer.data(), 1, buffer.size(), fp));
+ fclose(fp);
+
+ BufferMaps maps(buffer.data());
+ ASSERT_TRUE(maps.Parse());
+
+ ASSERT_EQ(ARCH_X86, regs.Arch());
+
+ std::shared_ptr<Memory> process_memory(memory);
+
+ char* cwd = getcwd(nullptr, 0);
+ ASSERT_EQ(0, chdir(dir.c_str()));
+ JitDebug jit_debug(process_memory);
+ Unwinder unwinder(128, &maps, ®s, process_memory);
+ unwinder.SetJitDebug(&jit_debug, regs.Arch());
+ unwinder.Unwind();
+ ASSERT_EQ(0, chdir(cwd));
+ free(cwd);
+
+ std::string frame_info(DumpFrames(unwinder));
+ ASSERT_EQ(69U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+ EXPECT_EQ(
+ " #00 pc 00068fb8 libarttestd.so (_ZN3artL13CauseSegfaultEv+72)\n"
+ " #01 pc 00067f00 libarttestd.so (Java_Main_unwindInProcess+10032)\n"
+ " #02 pc 000021a8 (offset 0x2000) 137-cfi.odex (boolean Main.unwindInProcess(boolean, int, "
+ "boolean)+136)\n"
+ " #03 pc 0000fe81 anonymous:ee74c000 (boolean Main.bar(boolean)+65)\n"
+ " #04 pc 006ad4d2 libartd.so (art_quick_invoke_stub+338)\n"
+ " #05 pc 00146ab5 libartd.so "
+ "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+885)\n"
+ " #06 pc 0039cf0d libartd.so "
+ "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
+ "11ShadowFrameEtPNS_6JValueE+653)\n"
+ " #07 pc 00392552 libartd.so "
+ "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
+ "6JValueEb+354)\n"
+ " #08 pc 0039399a libartd.so "
+ "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
+ "20CodeItemDataAccessorEPNS_11ShadowFrameE+234)\n"
+ " #09 pc 00684362 libartd.so (artQuickToInterpreterBridge+1058)\n"
+ " #10 pc 006b35bd libartd.so (art_quick_to_interpreter_bridge+77)\n"
+ " #11 pc 0000fe04 anonymous:ee74c000 (int Main.compare(Main, Main)+52)\n"
+ " #12 pc 006ad4d2 libartd.so (art_quick_invoke_stub+338)\n"
+ " #13 pc 00146ab5 libartd.so "
+ "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+885)\n"
+ " #14 pc 0039cf0d libartd.so "
+ "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
+ "11ShadowFrameEtPNS_6JValueE+653)\n"
+ " #15 pc 00392552 libartd.so "
+ "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
+ "6JValueEb+354)\n"
+ " #16 pc 0039399a libartd.so "
+ "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
+ "20CodeItemDataAccessorEPNS_11ShadowFrameE+234)\n"
+ " #17 pc 00684362 libartd.so (artQuickToInterpreterBridge+1058)\n"
+ " #18 pc 006b35bd libartd.so (art_quick_to_interpreter_bridge+77)\n"
+ " #19 pc 0000fd3c anonymous:ee74c000 (int Main.compare(java.lang.Object, "
+ "java.lang.Object)+108)\n"
+ " #20 pc 006ad4d2 libartd.so (art_quick_invoke_stub+338)\n"
+ " #21 pc 00146ab5 libartd.so "
+ "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+885)\n"
+ " #22 pc 0039cf0d libartd.so "
+ "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
+ "11ShadowFrameEtPNS_6JValueE+653)\n"
+ " #23 pc 00392552 libartd.so "
+ "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
+ "6JValueEb+354)\n"
+ " #24 pc 0039399a libartd.so "
+ "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
+ "20CodeItemDataAccessorEPNS_11ShadowFrameE+234)\n"
+ " #25 pc 00684362 libartd.so (artQuickToInterpreterBridge+1058)\n"
+ " #26 pc 006b35bd libartd.so (art_quick_to_interpreter_bridge+77)\n"
+ " #27 pc 0000fbdc anonymous:ee74c000 (int "
+ "java.util.Arrays.binarySearch0(java.lang.Object[], int, int, java.lang.Object, "
+ "java.util.Comparator)+332)\n"
+ " #28 pc 006ad6a2 libartd.so (art_quick_invoke_static_stub+418)\n"
+ " #29 pc 00146acb libartd.so "
+ "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+907)\n"
+ " #30 pc 0039cf0d libartd.so "
+ "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
+ "11ShadowFrameEtPNS_6JValueE+653)\n"
+ " #31 pc 00392552 libartd.so "
+ "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
+ "6JValueEb+354)\n"
+ " #32 pc 0039399a libartd.so "
+ "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
+ "20CodeItemDataAccessorEPNS_11ShadowFrameE+234)\n"
+ " #33 pc 00684362 libartd.so (artQuickToInterpreterBridge+1058)\n"
+ " #34 pc 006b35bd libartd.so (art_quick_to_interpreter_bridge+77)\n"
+ " #35 pc 0000f625 anonymous:ee74c000 (boolean Main.foo()+165)\n"
+ " #36 pc 006ad4d2 libartd.so (art_quick_invoke_stub+338)\n"
+ " #37 pc 00146ab5 libartd.so "
+ "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+885)\n"
+ " #38 pc 0039cf0d libartd.so "
+ "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
+ "11ShadowFrameEtPNS_6JValueE+653)\n"
+ " #39 pc 00392552 libartd.so "
+ "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
+ "6JValueEb+354)\n"
+ " #40 pc 0039399a libartd.so "
+ "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
+ "20CodeItemDataAccessorEPNS_11ShadowFrameE+234)\n"
+ " #41 pc 00684362 libartd.so (artQuickToInterpreterBridge+1058)\n"
+ " #42 pc 006b35bd libartd.so (art_quick_to_interpreter_bridge+77)\n"
+ " #43 pc 0000eedc anonymous:ee74c000 (void Main.runPrimary()+60)\n"
+ " #44 pc 006ad4d2 libartd.so (art_quick_invoke_stub+338)\n"
+ " #45 pc 00146ab5 libartd.so "
+ "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+885)\n"
+ " #46 pc 0039cf0d libartd.so "
+ "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
+ "11ShadowFrameEtPNS_6JValueE+653)\n"
+ " #47 pc 00392552 libartd.so "
+ "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
+ "6JValueEb+354)\n"
+ " #48 pc 0039399a libartd.so "
+ "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
+ "20CodeItemDataAccessorEPNS_11ShadowFrameE+234)\n"
+ " #49 pc 00684362 libartd.so (artQuickToInterpreterBridge+1058)\n"
+ " #50 pc 006b35bd libartd.so (art_quick_to_interpreter_bridge+77)\n"
+ " #51 pc 0000ac22 anonymous:ee74c000 (void Main.main(java.lang.String[])+98)\n"
+ " #52 pc 006ad6a2 libartd.so (art_quick_invoke_static_stub+418)\n"
+ " #53 pc 00146acb libartd.so "
+ "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+907)\n"
+ " #54 pc 0039cf0d libartd.so "
+ "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
+ "11ShadowFrameEtPNS_6JValueE+653)\n"
+ " #55 pc 00392552 libartd.so "
+ "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
+ "6JValueEb+354)\n"
+ " #56 pc 0039399a libartd.so "
+ "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
+ "20CodeItemDataAccessorEPNS_11ShadowFrameE+234)\n"
+ " #57 pc 00684362 libartd.so (artQuickToInterpreterBridge+1058)\n"
+ " #58 pc 006b35bd libartd.so (art_quick_to_interpreter_bridge+77)\n"
+ " #59 pc 006ad6a2 libartd.so (art_quick_invoke_static_stub+418)\n"
+ " #60 pc 00146acb libartd.so "
+ "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+907)\n"
+ " #61 pc 005aac95 libartd.so "
+ "(_ZN3artL18InvokeWithArgArrayERKNS_33ScopedObjectAccessAlreadyRunnableEPNS_9ArtMethodEPNS_"
+ "8ArgArrayEPNS_6JValueEPKc+85)\n"
+ " #62 pc 005aab5a libartd.so "
+ "(_ZN3art17InvokeWithVarArgsERKNS_33ScopedObjectAccessAlreadyRunnableEP8_jobjectP10_"
+ "jmethodIDPc+362)\n"
+ " #63 pc 0048a3dd libartd.so "
+ "(_ZN3art3JNI21CallStaticVoidMethodVEP7_JNIEnvP7_jclassP10_jmethodIDPc+125)\n"
+ " #64 pc 0018448c libartd.so "
+ "(_ZN3art8CheckJNI11CallMethodVEPKcP7_JNIEnvP8_jobjectP7_jclassP10_jmethodIDPcNS_"
+ "9Primitive4TypeENS_10InvokeTypeE+1964)\n"
+ " #65 pc 0017cf06 libartd.so "
+ "(_ZN3art8CheckJNI21CallStaticVoidMethodVEP7_JNIEnvP7_jclassP10_jmethodIDPc+70)\n"
+ " #66 pc 00001d8c dalvikvm32 "
+ "(_ZN7_JNIEnv20CallStaticVoidMethodEP7_jclassP10_jmethodIDz+60)\n"
+ " #67 pc 00001a80 dalvikvm32 (main+1312)\n"
+ " #68 pc 00018275 libc.so\n",
+ frame_info);
+}
+
+TEST(UnwindOfflineTest, jit_debug_arm32) {
+ std::string dir(TestGetFileDirectory() + "offline/jit_debug_arm32/");
+
+ MemoryOfflineParts* memory = new MemoryOfflineParts;
+ AddMemory(dir + "descriptor.data", memory);
+ AddMemory(dir + "descriptor1.data", memory);
+ AddMemory(dir + "stack.data", memory);
+ for (size_t i = 0; i < 7; i++) {
+ AddMemory(dir + "entry" + std::to_string(i) + ".data", memory);
+ AddMemory(dir + "jit" + std::to_string(i) + ".data", memory);
+ }
+
+ FILE* fp = fopen((dir + "regs.txt").c_str(), "r");
+ ASSERT_TRUE(fp != nullptr);
+ RegsArm regs;
+ uint64_t reg_value;
+ ASSERT_EQ(1, fscanf(fp, "r0: %" SCNx64 "\n", ®_value));
+ regs[ARM_REG_R0] = reg_value;
+ ASSERT_EQ(1, fscanf(fp, "r1: %" SCNx64 "\n", ®_value));
+ regs[ARM_REG_R1] = reg_value;
+ ASSERT_EQ(1, fscanf(fp, "r2: %" SCNx64 "\n", ®_value));
+ regs[ARM_REG_R2] = reg_value;
+ ASSERT_EQ(1, fscanf(fp, "r3: %" SCNx64 "\n", ®_value));
+ regs[ARM_REG_R3] = reg_value;
+ ASSERT_EQ(1, fscanf(fp, "r4: %" SCNx64 "\n", ®_value));
+ regs[ARM_REG_R4] = reg_value;
+ ASSERT_EQ(1, fscanf(fp, "r5: %" SCNx64 "\n", ®_value));
+ regs[ARM_REG_R5] = reg_value;
+ ASSERT_EQ(1, fscanf(fp, "r6: %" SCNx64 "\n", ®_value));
+ regs[ARM_REG_R6] = reg_value;
+ ASSERT_EQ(1, fscanf(fp, "r7: %" SCNx64 "\n", ®_value));
+ regs[ARM_REG_R7] = reg_value;
+ ASSERT_EQ(1, fscanf(fp, "r8: %" SCNx64 "\n", ®_value));
+ regs[ARM_REG_R8] = reg_value;
+ ASSERT_EQ(1, fscanf(fp, "r9: %" SCNx64 "\n", ®_value));
+ regs[ARM_REG_R9] = reg_value;
+ ASSERT_EQ(1, fscanf(fp, "r10: %" SCNx64 "\n", ®_value));
+ regs[ARM_REG_R10] = reg_value;
+ ASSERT_EQ(1, fscanf(fp, "r11: %" SCNx64 "\n", ®_value));
+ regs[ARM_REG_R11] = reg_value;
+ ASSERT_EQ(1, fscanf(fp, "ip: %" SCNx64 "\n", ®_value));
+ regs[ARM_REG_R12] = reg_value;
+ ASSERT_EQ(1, fscanf(fp, "sp: %" SCNx64 "\n", ®_value));
+ regs[ARM_REG_SP] = reg_value;
+ ASSERT_EQ(1, fscanf(fp, "lr: %" SCNx64 "\n", ®_value));
+ regs[ARM_REG_LR] = reg_value;
+ ASSERT_EQ(1, fscanf(fp, "pc: %" SCNx64 "\n", ®_value));
+ regs[ARM_REG_PC] = reg_value;
+ regs.SetFromRaw();
+ fclose(fp);
+
+ fp = fopen((dir + "maps.txt").c_str(), "r");
+ ASSERT_TRUE(fp != nullptr);
+ // The file is guaranteed to be less than 4096 bytes.
+ std::vector<char> buffer(4096);
+ ASSERT_NE(0U, fread(buffer.data(), 1, buffer.size(), fp));
+ fclose(fp);
+
+ BufferMaps maps(buffer.data());
+ ASSERT_TRUE(maps.Parse());
+
+ ASSERT_EQ(ARCH_ARM, regs.Arch());
+
+ std::shared_ptr<Memory> process_memory(memory);
+
+ char* cwd = getcwd(nullptr, 0);
+ ASSERT_EQ(0, chdir(dir.c_str()));
+ JitDebug jit_debug(process_memory);
+ Unwinder unwinder(128, &maps, ®s, process_memory);
+ unwinder.SetJitDebug(&jit_debug, regs.Arch());
+ unwinder.Unwind();
+ ASSERT_EQ(0, chdir(cwd));
+ free(cwd);
+
+ std::string frame_info(DumpFrames(unwinder));
+ ASSERT_EQ(76U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
+ EXPECT_EQ(
+ " #00 pc 00018a5e libarttestd.so (Java_Main_unwindInProcess+865)\n"
+ " #01 pc 0000212d (offset 0x2000) 137-cfi.odex (boolean Main.unwindInProcess(boolean, int, "
+ "boolean)+92)\n"
+ " #02 pc 00011cb1 anonymous:e2796000 (boolean Main.bar(boolean)+72)\n"
+ " #03 pc 00462175 libartd.so (art_quick_invoke_stub_internal+68)\n"
+ " #04 pc 00467129 libartd.so (art_quick_invoke_stub+228)\n"
+ " #05 pc 000bf7a9 libartd.so "
+ "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+864)\n"
+ " #06 pc 00247833 libartd.so "
+ "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
+ "11ShadowFrameEtPNS_6JValueE+382)\n"
+ " #07 pc 0022e935 libartd.so "
+ "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
+ "6JValueEb+244)\n"
+ " #08 pc 0022f71d libartd.so "
+ "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
+ "20CodeItemDataAccessorEPNS_11ShadowFrameE+128)\n"
+ " #09 pc 00442865 libartd.so (artQuickToInterpreterBridge+796)\n"
+ " #10 pc 004666ff libartd.so (art_quick_to_interpreter_bridge+30)\n"
+ " #11 pc 00011c31 anonymous:e2796000 (int Main.compare(Main, Main)+64)\n"
+ " #12 pc 00462175 libartd.so (art_quick_invoke_stub_internal+68)\n"
+ " #13 pc 00467129 libartd.so (art_quick_invoke_stub+228)\n"
+ " #14 pc 000bf7a9 libartd.so "
+ "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+864)\n"
+ " #15 pc 00247833 libartd.so "
+ "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
+ "11ShadowFrameEtPNS_6JValueE+382)\n"
+ " #16 pc 0022e935 libartd.so "
+ "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
+ "6JValueEb+244)\n"
+ " #17 pc 0022f71d libartd.so "
+ "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
+ "20CodeItemDataAccessorEPNS_11ShadowFrameE+128)\n"
+ " #18 pc 00442865 libartd.so (artQuickToInterpreterBridge+796)\n"
+ " #19 pc 004666ff libartd.so (art_quick_to_interpreter_bridge+30)\n"
+ " #20 pc 00011b77 anonymous:e2796000 (int Main.compare(java.lang.Object, "
+ "java.lang.Object)+118)\n"
+ " #21 pc 00462175 libartd.so (art_quick_invoke_stub_internal+68)\n"
+ " #22 pc 00467129 libartd.so (art_quick_invoke_stub+228)\n"
+ " #23 pc 000bf7a9 libartd.so "
+ "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+864)\n"
+ " #24 pc 00247833 libartd.so "
+ "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
+ "11ShadowFrameEtPNS_6JValueE+382)\n"
+ " #25 pc 0022e935 libartd.so "
+ "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
+ "6JValueEb+244)\n"
+ " #26 pc 0022f71d libartd.so "
+ "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
+ "20CodeItemDataAccessorEPNS_11ShadowFrameE+128)\n"
+ " #27 pc 00442865 libartd.so (artQuickToInterpreterBridge+796)\n"
+ " #28 pc 004666ff libartd.so (art_quick_to_interpreter_bridge+30)\n"
+ " #29 pc 00011a29 anonymous:e2796000 (int "
+ "java.util.Arrays.binarySearch0(java.lang.Object[], int, int, java.lang.Object, "
+ "java.util.Comparator)+304)\n"
+ " #30 pc 00462175 libartd.so (art_quick_invoke_stub_internal+68)\n"
+ " #31 pc 0046722f libartd.so (art_quick_invoke_static_stub+226)\n"
+ " #32 pc 000bf7bb libartd.so "
+ "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+882)\n"
+ " #33 pc 00247833 libartd.so "
+ "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
+ "11ShadowFrameEtPNS_6JValueE+382)\n"
+ " #34 pc 0022e935 libartd.so "
+ "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
+ "6JValueEb+244)\n"
+ " #35 pc 0022f71d libartd.so "
+ "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
+ "20CodeItemDataAccessorEPNS_11ShadowFrameE+128)\n"
+ " #36 pc 00442865 libartd.so (artQuickToInterpreterBridge+796)\n"
+ " #37 pc 004666ff libartd.so (art_quick_to_interpreter_bridge+30)\n"
+ " #38 pc 0001139b anonymous:e2796000 (boolean Main.foo()+178)\n"
+ " #39 pc 00462175 libartd.so (art_quick_invoke_stub_internal+68)\n"
+ " #40 pc 00467129 libartd.so (art_quick_invoke_stub+228)\n"
+ " #41 pc 000bf7a9 libartd.so "
+ "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+864)\n"
+ " #42 pc 00247833 libartd.so "
+ "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
+ "11ShadowFrameEtPNS_6JValueE+382)\n"
+ " #43 pc 0022e935 libartd.so "
+ "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
+ "6JValueEb+244)\n"
+ " #44 pc 0022f71d libartd.so "
+ "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
+ "20CodeItemDataAccessorEPNS_11ShadowFrameE+128)\n"
+ " #45 pc 00442865 libartd.so (artQuickToInterpreterBridge+796)\n"
+ " #46 pc 004666ff libartd.so (art_quick_to_interpreter_bridge+30)\n"
+ " #47 pc 00010aa7 anonymous:e2796000 (void Main.runPrimary()+70)\n"
+ " #48 pc 00462175 libartd.so (art_quick_invoke_stub_internal+68)\n"
+ " #49 pc 00467129 libartd.so (art_quick_invoke_stub+228)\n"
+ " #50 pc 000bf7a9 libartd.so "
+ "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+864)\n"
+ " #51 pc 00247833 libartd.so "
+ "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
+ "11ShadowFrameEtPNS_6JValueE+382)\n"
+ " #52 pc 0022e935 libartd.so "
+ "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
+ "6JValueEb+244)\n"
+ " #53 pc 0022f71d libartd.so "
+ "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
+ "20CodeItemDataAccessorEPNS_11ShadowFrameE+128)\n"
+ " #54 pc 00442865 libartd.so (artQuickToInterpreterBridge+796)\n"
+ " #55 pc 004666ff libartd.so (art_quick_to_interpreter_bridge+30)\n"
+ " #56 pc 0000ba99 anonymous:e2796000 (void Main.main(java.lang.String[])+144)\n"
+ " #57 pc 00462175 libartd.so (art_quick_invoke_stub_internal+68)\n"
+ " #58 pc 0046722f libartd.so (art_quick_invoke_static_stub+226)\n"
+ " #59 pc 000bf7bb libartd.so "
+ "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+882)\n"
+ " #60 pc 00247833 libartd.so "
+ "(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
+ "11ShadowFrameEtPNS_6JValueE+382)\n"
+ " #61 pc 0022e935 libartd.so "
+ "(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
+ "6JValueEb+244)\n"
+ " #62 pc 0022f71d libartd.so "
+ "(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
+ "20CodeItemDataAccessorEPNS_11ShadowFrameE+128)\n"
+ " #63 pc 00442865 libartd.so (artQuickToInterpreterBridge+796)\n"
+ " #64 pc 004666ff libartd.so (art_quick_to_interpreter_bridge+30)\n"
+ " #65 pc 00462175 libartd.so (art_quick_invoke_stub_internal+68)\n"
+ " #66 pc 0046722f libartd.so (art_quick_invoke_static_stub+226)\n"
+ " #67 pc 000bf7bb libartd.so "
+ "(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+882)\n"
+ " #68 pc 003b292d libartd.so "
+ "(_ZN3artL18InvokeWithArgArrayERKNS_33ScopedObjectAccessAlreadyRunnableEPNS_9ArtMethodEPNS_"
+ "8ArgArrayEPNS_6JValueEPKc+52)\n"
+ " #69 pc 003b26c3 libartd.so "
+ "(_ZN3art17InvokeWithVarArgsERKNS_33ScopedObjectAccessAlreadyRunnableEP8_jobjectP10_"
+ "jmethodIDSt9__va_list+210)\n"
+ " #70 pc 00308411 libartd.so "
+ "(_ZN3art3JNI21CallStaticVoidMethodVEP7_JNIEnvP7_jclassP10_jmethodIDSt9__va_list+76)\n"
+ " #71 pc 000e6a9f libartd.so "
+ "(_ZN3art8CheckJNI11CallMethodVEPKcP7_JNIEnvP8_jobjectP7_jclassP10_jmethodIDSt9__va_listNS_"
+ "9Primitive4TypeENS_10InvokeTypeE+1486)\n"
+ " #72 pc 000e19b9 libartd.so "
+ "(_ZN3art8CheckJNI21CallStaticVoidMethodVEP7_JNIEnvP7_jclassP10_jmethodIDSt9__va_list+40)\n"
+ " #73 pc 0000159f dalvikvm32 "
+ "(_ZN7_JNIEnv20CallStaticVoidMethodEP7_jclassP10_jmethodIDz+30)\n"
+ " #74 pc 00001349 dalvikvm32 (main+896)\n"
+ " #75 pc 000850c9 libc.so\n",
+ frame_info);
+}
+
} // namespace unwindstack
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm32/137-cfi.odex b/libunwindstack/tests/files/offline/jit_debug_arm32/137-cfi.odex
new file mode 100644
index 0000000..35a6bc5
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_arm32/137-cfi.odex
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm32/dalvikvm32 b/libunwindstack/tests/files/offline/jit_debug_arm32/dalvikvm32
new file mode 100644
index 0000000..def299e
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_arm32/dalvikvm32
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm32/descriptor.data b/libunwindstack/tests/files/offline/jit_debug_arm32/descriptor.data
new file mode 100644
index 0000000..7b876b5
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_arm32/descriptor.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm32/descriptor1.data b/libunwindstack/tests/files/offline/jit_debug_arm32/descriptor1.data
new file mode 100644
index 0000000..3c468d6
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_arm32/descriptor1.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm32/entry0.data b/libunwindstack/tests/files/offline/jit_debug_arm32/entry0.data
new file mode 100644
index 0000000..2c7689b
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_arm32/entry0.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm32/entry1.data b/libunwindstack/tests/files/offline/jit_debug_arm32/entry1.data
new file mode 100644
index 0000000..22a35b8
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_arm32/entry1.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm32/entry2.data b/libunwindstack/tests/files/offline/jit_debug_arm32/entry2.data
new file mode 100644
index 0000000..61f3927
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_arm32/entry2.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm32/entry3.data b/libunwindstack/tests/files/offline/jit_debug_arm32/entry3.data
new file mode 100644
index 0000000..1a37628
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_arm32/entry3.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm32/entry4.data b/libunwindstack/tests/files/offline/jit_debug_arm32/entry4.data
new file mode 100644
index 0000000..7ef62ca
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_arm32/entry4.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm32/entry5.data b/libunwindstack/tests/files/offline/jit_debug_arm32/entry5.data
new file mode 100644
index 0000000..6d27c89
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_arm32/entry5.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm32/entry6.data b/libunwindstack/tests/files/offline/jit_debug_arm32/entry6.data
new file mode 100644
index 0000000..bfbceea
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_arm32/entry6.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm32/jit0.data b/libunwindstack/tests/files/offline/jit_debug_arm32/jit0.data
new file mode 100644
index 0000000..b78848e
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_arm32/jit0.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm32/jit1.data b/libunwindstack/tests/files/offline/jit_debug_arm32/jit1.data
new file mode 100644
index 0000000..8f927ac
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_arm32/jit1.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm32/jit2.data b/libunwindstack/tests/files/offline/jit_debug_arm32/jit2.data
new file mode 100644
index 0000000..1d1dfca
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_arm32/jit2.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm32/jit3.data b/libunwindstack/tests/files/offline/jit_debug_arm32/jit3.data
new file mode 100644
index 0000000..89aeb43
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_arm32/jit3.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm32/jit4.data b/libunwindstack/tests/files/offline/jit_debug_arm32/jit4.data
new file mode 100644
index 0000000..e076934
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_arm32/jit4.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm32/jit5.data b/libunwindstack/tests/files/offline/jit_debug_arm32/jit5.data
new file mode 100644
index 0000000..17d6041
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_arm32/jit5.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm32/jit6.data b/libunwindstack/tests/files/offline/jit_debug_arm32/jit6.data
new file mode 100644
index 0000000..aaff037
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_arm32/jit6.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm32/libart.so b/libunwindstack/tests/files/offline/jit_debug_arm32/libart.so
new file mode 100644
index 0000000..0527893
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_arm32/libart.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm32/libartd.so b/libunwindstack/tests/files/offline/jit_debug_arm32/libartd.so
new file mode 100644
index 0000000..8559056
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_arm32/libartd.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm32/libarttestd.so b/libunwindstack/tests/files/offline/jit_debug_arm32/libarttestd.so
new file mode 100644
index 0000000..06dbf10
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_arm32/libarttestd.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm32/libc.so b/libunwindstack/tests/files/offline/jit_debug_arm32/libc.so
new file mode 100644
index 0000000..9894e66
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_arm32/libc.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm32/maps.txt b/libunwindstack/tests/files/offline/jit_debug_arm32/maps.txt
new file mode 100644
index 0000000..f25c781
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_arm32/maps.txt
@@ -0,0 +1,8 @@
+ab0d3000-ab0d8000 r-xp 0 00:00 0 dalvikvm32
+dfe4e000-dfe7b000 r-xp 0 00:00 0 libarttestd.so
+e0447000-e0448000 r-xp 2000 00:00 0 137-cfi.odex
+e2796000-e4796000 r-xp 0 00:00 0 anonymous:e2796000
+e648e000-e690f000 r-xp 00000000 00:00 0 libart.so
+ed306000-ed801000 r-xp 0 00:00 0 libartd.so
+eda88000-edb23000 r-xp 0 00:00 0 libc.so
+ede4e000-ede50000 r-xp 0 00:00 0 anonymous:ede4e000
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm32/regs.txt b/libunwindstack/tests/files/offline/jit_debug_arm32/regs.txt
new file mode 100644
index 0000000..0e20066
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_arm32/regs.txt
@@ -0,0 +1,16 @@
+r0: dfe7c0f8
+r1: 0
+r2: 0
+r3: 40000000
+r4: e051ffb4
+r5: 0
+r6: e051ffc0
+r7: ede514e8
+r8: ff85d1a8
+r9: ed9210c0
+r10: 58
+r11: 0
+ip: edb26d04
+sp: ff85d180
+lr: edaff5af
+pc: dfe66a5e
diff --git a/libunwindstack/tests/files/offline/jit_debug_arm32/stack.data b/libunwindstack/tests/files/offline/jit_debug_arm32/stack.data
new file mode 100644
index 0000000..b2ff14e
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_arm32/stack.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/137-cfi.odex b/libunwindstack/tests/files/offline/jit_debug_x86_32/137-cfi.odex
new file mode 100644
index 0000000..870ac0a
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86_32/137-cfi.odex
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/dalvikvm32 b/libunwindstack/tests/files/offline/jit_debug_x86_32/dalvikvm32
new file mode 100644
index 0000000..76ffad9
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86_32/dalvikvm32
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/descriptor.data b/libunwindstack/tests/files/offline/jit_debug_x86_32/descriptor.data
new file mode 100644
index 0000000..466dae2
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86_32/descriptor.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/entry0.data b/libunwindstack/tests/files/offline/jit_debug_x86_32/entry0.data
new file mode 100644
index 0000000..3a725e8
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86_32/entry0.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/entry1.data b/libunwindstack/tests/files/offline/jit_debug_x86_32/entry1.data
new file mode 100644
index 0000000..767550f
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86_32/entry1.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/entry2.data b/libunwindstack/tests/files/offline/jit_debug_x86_32/entry2.data
new file mode 100644
index 0000000..e7e492e
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86_32/entry2.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/entry3.data b/libunwindstack/tests/files/offline/jit_debug_x86_32/entry3.data
new file mode 100644
index 0000000..65f9cd4
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86_32/entry3.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/entry4.data b/libunwindstack/tests/files/offline/jit_debug_x86_32/entry4.data
new file mode 100644
index 0000000..30aa28c
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86_32/entry4.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/entry5.data b/libunwindstack/tests/files/offline/jit_debug_x86_32/entry5.data
new file mode 100644
index 0000000..3c89673
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86_32/entry5.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/entry6.data b/libunwindstack/tests/files/offline/jit_debug_x86_32/entry6.data
new file mode 100644
index 0000000..9c9b83c
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86_32/entry6.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/jit0.data b/libunwindstack/tests/files/offline/jit_debug_x86_32/jit0.data
new file mode 100644
index 0000000..eaad142
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86_32/jit0.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/jit1.data b/libunwindstack/tests/files/offline/jit_debug_x86_32/jit1.data
new file mode 100644
index 0000000..d534816
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86_32/jit1.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/jit2.data b/libunwindstack/tests/files/offline/jit_debug_x86_32/jit2.data
new file mode 100644
index 0000000..dbeb886
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86_32/jit2.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/jit3.data b/libunwindstack/tests/files/offline/jit_debug_x86_32/jit3.data
new file mode 100644
index 0000000..bf2142d
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86_32/jit3.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/jit4.data b/libunwindstack/tests/files/offline/jit_debug_x86_32/jit4.data
new file mode 100644
index 0000000..e2ba1b0
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86_32/jit4.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/jit5.data b/libunwindstack/tests/files/offline/jit_debug_x86_32/jit5.data
new file mode 100644
index 0000000..c27ba54
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86_32/jit5.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/jit6.data b/libunwindstack/tests/files/offline/jit_debug_x86_32/jit6.data
new file mode 100644
index 0000000..5fc8fae
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86_32/jit6.data
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/libarttestd.so b/libunwindstack/tests/files/offline/jit_debug_x86_32/libarttestd.so
new file mode 100644
index 0000000..e72e673
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86_32/libarttestd.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/libc.so b/libunwindstack/tests/files/offline/jit_debug_x86_32/libc.so
new file mode 100644
index 0000000..9c78790
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86_32/libc.so
Binary files differ
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/maps.txt b/libunwindstack/tests/files/offline/jit_debug_x86_32/maps.txt
new file mode 100644
index 0000000..db4f9f7
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86_32/maps.txt
@@ -0,0 +1,6 @@
+56573000-56577000 r-xp 0 00:00 0 dalvikvm32
+eb833000-eb8cc000 r-xp 0 00:00 0 libarttestd.so
+ec606000-ec607000 r-xp 2000 00:00 0 137-cfi.odex
+ee74c000-f074c000 r-xp 0 00:00 0 anonymous:ee74c000
+f6be1000-f732b000 r-xp 0 00:00 0 libartd.so
+f734b000-f74fc000 r-xp 0 00:00 0 libc.so
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/regs.txt b/libunwindstack/tests/files/offline/jit_debug_x86_32/regs.txt
new file mode 100644
index 0000000..f68305b
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86_32/regs.txt
@@ -0,0 +1,9 @@
+eax: eb8cccd0
+ebx: eb8cccd0
+ecx: ff
+edx: ffeb2ca8
+ebp: ffeb5298
+edi: ffeb5c08
+esi: ffeb5c00
+esp: ffeb5280
+eip: eb89bfb8
diff --git a/libunwindstack/tests/files/offline/jit_debug_x86_32/stack.data b/libunwindstack/tests/files/offline/jit_debug_x86_32/stack.data
new file mode 100644
index 0000000..c345762
--- /dev/null
+++ b/libunwindstack/tests/files/offline/jit_debug_x86_32/stack.data
Binary files differ
diff --git a/libunwindstack/tools/unwind.cpp b/libunwindstack/tools/unwind.cpp
index 81bedb7..33e5527 100644
--- a/libunwindstack/tools/unwind.cpp
+++ b/libunwindstack/tools/unwind.cpp
@@ -27,6 +27,7 @@
#include <unistd.h>
#include <unwindstack/Elf.h>
+#include <unwindstack/JitDebug.h>
#include <unwindstack/Maps.h>
#include <unwindstack/Memory.h>
#include <unwindstack/Regs.h>
@@ -90,11 +91,18 @@
auto process_memory = unwindstack::Memory::CreateProcessMemory(pid);
unwindstack::Unwinder unwinder(128, &remote_maps, regs, process_memory);
+ unwindstack::JitDebug jit_debug(process_memory);
+ unwinder.SetJitDebug(&jit_debug, regs->Arch());
unwinder.Unwind();
// Print the frames.
+ const std::vector<unwindstack::FrameData>& frames = unwinder.frames();
for (size_t i = 0; i < unwinder.NumFrames(); i++) {
printf("%s\n", unwinder.FormatFrame(i).c_str());
+ const unwindstack::FrameData* frame = &frames[i];
+ if (frame->dex_pc != 0) {
+ printf(" dex pc %" PRIx64 "\n", frame->dex_pc);
+ }
}
}
diff --git a/libunwindstack/tools/unwind_for_offline.cpp b/libunwindstack/tools/unwind_for_offline.cpp
index d64ef8f..74868d4 100644
--- a/libunwindstack/tools/unwind_for_offline.cpp
+++ b/libunwindstack/tools/unwind_for_offline.cpp
@@ -33,6 +33,7 @@
#include <vector>
#include <unwindstack/Elf.h>
+#include <unwindstack/JitDebug.h>
#include <unwindstack/Maps.h>
#include <unwindstack/Memory.h>
#include <unwindstack/Regs.h>
@@ -191,7 +192,9 @@
// elf files are involved.
uint64_t sp = regs->sp();
auto process_memory = unwindstack::Memory::CreateProcessMemory(pid);
+ unwindstack::JitDebug jit_debug(process_memory);
unwindstack::Unwinder unwinder(1024, &maps, regs, process_memory);
+ unwinder.SetJitDebug(&jit_debug, regs->Arch());
unwinder.Unwind();
std::unordered_map<uint64_t, map_info_t> maps_by_start;
@@ -214,6 +217,10 @@
}
}
+ for (size_t i = 0; i < unwinder.NumFrames(); i++) {
+ printf("%s\n", unwinder.FormatFrame(i).c_str());
+ }
+
if (!SaveStack(pid, sp, last_sp)) {
return 1;
}
diff --git a/libutils/Android.bp b/libutils/Android.bp
index 6b50f0c..3bca6c8 100644
--- a/libutils/Android.bp
+++ b/libutils/Android.bp
@@ -43,8 +43,8 @@
},
}
-cc_library {
- name: "libutils",
+cc_defaults {
+ name: "libutils_defaults",
vendor_available: true,
vndk: {
enabled: true,
@@ -52,29 +52,6 @@
},
host_supported: true,
- srcs: [
- "CallStack.cpp",
- "FileMap.cpp",
- "JenkinsHash.cpp",
- "NativeHandle.cpp",
- "Printer.cpp",
- "PropertyMap.cpp",
- "RefBase.cpp",
- "SharedBuffer.cpp",
- "Static.cpp",
- "StopWatch.cpp",
- "String8.cpp",
- "String16.cpp",
- "StrongPointer.cpp",
- "SystemClock.cpp",
- "Threads.cpp",
- "Timers.cpp",
- "Tokenizer.cpp",
- "Unicode.cpp",
- "VectorImpl.cpp",
- "misc.cpp",
- ],
-
cflags: ["-Wall", "-Werror"],
include_dirs: ["external/safe-iop/include"],
header_libs: [
@@ -96,14 +73,9 @@
target: {
android: {
- srcs: [
- "Trace.cpp",
- ],
-
cflags: ["-fvisibility=protected"],
shared_libs: [
- "libbacktrace",
"libcutils",
"libdl",
"libvndksupport",
@@ -113,12 +85,6 @@
misc_undefined: ["integer"],
},
},
- linux: {
- srcs: [
- "Looper.cpp",
- "ProcessCallStack.cpp",
- ],
- },
host: {
cflags: ["-DLIBUTILS_NATIVE=1"],
@@ -148,6 +114,75 @@
},
}
+cc_library {
+ name: "libutils",
+ defaults: ["libutils_defaults"],
+
+ srcs: [
+ "FileMap.cpp",
+ "JenkinsHash.cpp",
+ "NativeHandle.cpp",
+ "Printer.cpp",
+ "PropertyMap.cpp",
+ "RefBase.cpp",
+ "SharedBuffer.cpp",
+ "Static.cpp",
+ "StopWatch.cpp",
+ "String8.cpp",
+ "String16.cpp",
+ "StrongPointer.cpp",
+ "SystemClock.cpp",
+ "Threads.cpp",
+ "Timers.cpp",
+ "Tokenizer.cpp",
+ "Unicode.cpp",
+ "VectorImpl.cpp",
+ "misc.cpp",
+ ],
+
+ target: {
+ android: {
+ srcs: [
+ "Trace.cpp",
+ ],
+ },
+ linux: {
+ srcs: [
+ "Looper.cpp",
+ ],
+ },
+ },
+}
+
+cc_library {
+ name: "libutilscallstack",
+ defaults: ["libutils_defaults"],
+
+ srcs: [
+ "CallStack.cpp",
+ ],
+
+ arch: {
+ mips: {
+ cflags: ["-DALIGN_DOUBLE"],
+ },
+ },
+
+ target: {
+ android: {
+ shared_libs: [
+ "libutils",
+ "libbacktrace",
+ ],
+ },
+ linux: {
+ srcs: [
+ "ProcessCallStack.cpp",
+ ],
+ },
+ },
+}
+
// Include subdirectory makefiles
// ============================================================
diff --git a/lmkd/Android.bp b/lmkd/Android.bp
index 3f8a503..76d308a 100644
--- a/lmkd/Android.bp
+++ b/lmkd/Android.bp
@@ -4,10 +4,17 @@
srcs: ["lmkd.c"],
shared_libs: [
"liblog",
- "libprocessgroup",
"libcutils",
],
cflags: ["-Werror"],
init_rc: ["lmkd.rc"],
+
+ product_variables: {
+ debuggable: {
+ cflags: [
+ "-DLMKD_TRACE_KILLS"
+ ],
+ },
+ },
}
diff --git a/lmkd/lmkd.c b/lmkd/lmkd.c
index 25d3170..1b8fed3 100644
--- a/lmkd/lmkd.c
+++ b/lmkd/lmkd.c
@@ -29,13 +29,30 @@
#include <sys/mman.h>
#include <sys/socket.h>
#include <sys/types.h>
-#include <time.h>
#include <unistd.h>
#include <cutils/properties.h>
#include <cutils/sockets.h>
#include <log/log.h>
-#include <processgroup/processgroup.h>
+
+/*
+ * Define LMKD_TRACE_KILLS to record lmkd kills in kernel traces
+ * to profile and correlate with OOM kills
+ */
+#ifdef LMKD_TRACE_KILLS
+
+#define ATRACE_TAG ATRACE_TAG_ALWAYS
+#include <cutils/trace.h>
+
+#define TRACE_KILL_START(pid) ATRACE_INT(__FUNCTION__, pid);
+#define TRACE_KILL_END() ATRACE_INT(__FUNCTION__, 0);
+
+#else /* LMKD_TRACE_KILLS */
+
+#define TRACE_KILL_START(pid)
+#define TRACE_KILL_END()
+
+#endif /* LMKD_TRACE_KILLS */
#ifndef __unused
#define __unused __attribute__((__unused__))
@@ -85,12 +102,13 @@
};
static int level_oomadj[VMPRESS_LEVEL_COUNT];
-static int mpevfd[VMPRESS_LEVEL_COUNT];
+static int mpevfd[VMPRESS_LEVEL_COUNT] = { -1, -1, -1 };
static bool debug_process_killing;
static bool enable_pressure_upgrade;
static int64_t upgrade_pressure;
static int64_t downgrade_pressure;
static bool is_go_device;
+static bool kill_heaviest_task;
/* control socket listen and data */
static int ctrl_lfd;
@@ -593,6 +611,29 @@
return (struct proc *)adjslot_tail(&procadjslot_list[ADJTOSLOT(oomadj)]);
}
+static struct proc *proc_get_heaviest(int oomadj) {
+ struct adjslot_list *head = &procadjslot_list[ADJTOSLOT(oomadj)];
+ struct adjslot_list *curr = head->next;
+ struct proc *maxprocp = NULL;
+ int maxsize = 0;
+ while (curr != head) {
+ int pid = ((struct proc *)curr)->pid;
+ int tasksize = proc_get_size(pid);
+ if (tasksize <= 0) {
+ struct adjslot_list *next = curr->next;
+ pid_remove(pid);
+ curr = next;
+ } else {
+ if (tasksize > maxsize) {
+ maxsize = tasksize;
+ maxprocp = (struct proc *)curr;
+ }
+ curr = curr->next;
+ }
+ }
+ return maxprocp;
+}
+
/* Kill one process specified by procp. Returns the size of the process killed */
static int kill_one_process(struct proc* procp, int min_score_adj,
enum vmpressure_level level) {
@@ -614,6 +655,8 @@
return -1;
}
+ TRACE_KILL_START(pid);
+
r = kill(pid, SIGKILL);
ALOGI(
"Killing '%s' (%d), uid %d, adj %d\n"
@@ -622,6 +665,8 @@
level_name[level], min_score_adj);
pid_remove(pid);
+ TRACE_KILL_END();
+
if (r) {
ALOGE("kill(%d): errno=%d", procp->pid, errno);
return -1;
@@ -643,7 +688,10 @@
struct proc *procp;
retry:
- procp = proc_adj_lru(i);
+ if (kill_heaviest_task)
+ procp = proc_get_heaviest(i);
+ else
+ procp = proc_adj_lru(i);
if (procp) {
killed_size = kill_one_process(procp, min_score_adj, level);
@@ -697,11 +745,20 @@
unsigned long long evcount;
int64_t mem_usage, memsw_usage;
int64_t mem_pressure;
+ enum vmpressure_level lvl;
- ret = read(mpevfd[level], &evcount, sizeof(evcount));
- if (ret < 0)
- ALOGE("Error reading memory pressure event fd; errno=%d",
- errno);
+ /*
+ * Check all event counters from low to critical
+ * and upgrade to the highest priority one. By reading
+ * eventfd we also reset the event counters.
+ */
+ for (lvl = VMPRESS_LEVEL_LOW; lvl < VMPRESS_LEVEL_COUNT; lvl++) {
+ if (mpevfd[lvl] != -1 &&
+ read(mpevfd[lvl], &evcount, sizeof(evcount)) > 0 &&
+ evcount > 0 && lvl > level) {
+ level = lvl;
+ }
+ }
mem_usage = get_memory_usage(MEMCG_MEMORY_USAGE);
memsw_usage = get_memory_usage(MEMCG_MEMORYSW_USAGE);
@@ -812,6 +869,7 @@
}
maxevents++;
mpevfd[level] = evfd;
+ close(evctlfd);
return true;
err:
@@ -921,9 +979,16 @@
level_oomadj[VMPRESS_LEVEL_CRITICAL] =
property_get_int32("ro.lmk.critical", 0);
debug_process_killing = property_get_bool("ro.lmk.debug", false);
- enable_pressure_upgrade = property_get_bool("ro.lmk.critical_upgrade", false);
- upgrade_pressure = (int64_t)property_get_int32("ro.lmk.upgrade_pressure", 50);
- downgrade_pressure = (int64_t)property_get_int32("ro.lmk.downgrade_pressure", 60);
+
+ /* By default disable upgrade/downgrade logic */
+ enable_pressure_upgrade =
+ property_get_bool("ro.lmk.critical_upgrade", false);
+ upgrade_pressure =
+ (int64_t)property_get_int32("ro.lmk.upgrade_pressure", 100);
+ downgrade_pressure =
+ (int64_t)property_get_int32("ro.lmk.downgrade_pressure", 100);
+ kill_heaviest_task =
+ property_get_bool("ro.lmk.kill_heaviest_task", true);
is_go_device = property_get_bool("ro.config.low_ram", false);
if (mlockall(MCL_CURRENT | MCL_FUTURE))
diff --git a/logd/README.property b/logd/README.property
index de6767a..da5f96f 100644
--- a/logd/README.property
+++ b/logd/README.property
@@ -31,9 +31,9 @@
resist increasing the log buffer.
persist.logd.size.<buffer> number ro Size of the buffer for <buffer> log
ro.logd.size.<buffer> number svelte default for persist.logd.size.<buffer>
-ro.config.low_ram bool false if true, logd.statistics, logd.kernel
- default false, logd.size 64K instead
- of 256K.
+ro.config.low_ram bool false if true, logd.statistics,
+ ro.logd.kernel default false,
+ logd.size 64K instead of 256K.
persist.logd.filter string Pruning filter to optimize content.
At runtime use: logcat -P "<string>"
ro.logd.filter string "~! ~1000/!" default for persist.logd.filter.
diff --git a/logd/main.cpp b/logd/main.cpp
index c8183f0..4af0d21 100644
--- a/logd/main.cpp
+++ b/logd/main.cpp
@@ -438,8 +438,8 @@
int fdPmesg = -1;
bool klogd = __android_logger_property_get_bool(
- "logd.kernel", BOOL_DEFAULT_TRUE | BOOL_DEFAULT_FLAG_PERSIST |
- BOOL_DEFAULT_FLAG_ENG | BOOL_DEFAULT_FLAG_SVELTE);
+ "ro.logd.kernel",
+ BOOL_DEFAULT_TRUE | BOOL_DEFAULT_FLAG_ENG | BOOL_DEFAULT_FLAG_SVELTE);
if (klogd) {
static const char proc_kmsg[] = "/proc/kmsg";
fdPmesg = android_get_control_file(proc_kmsg);
diff --git a/property_service/libpropertyinfoparser/Android.bp b/property_service/libpropertyinfoparser/Android.bp
index 39cd8ec..ea9b968 100644
--- a/property_service/libpropertyinfoparser/Android.bp
+++ b/property_service/libpropertyinfoparser/Android.bp
@@ -1,6 +1,7 @@
cc_library_static {
name: "libpropertyinfoparser",
host_supported: true,
+ vendor_available: true,
srcs: ["property_info_parser.cpp"],
cpp_std: "experimental",
diff --git a/property_service/libpropertyinfoparser/include/property_info_parser/property_info_parser.h b/property_service/libpropertyinfoparser/include/property_info_parser/property_info_parser.h
index 2ee8161..0548021 100644
--- a/property_service/libpropertyinfoparser/include/property_info_parser/property_info_parser.h
+++ b/property_service/libpropertyinfoparser/include/property_info_parser/property_info_parser.h
@@ -32,8 +32,8 @@
// This is the context match for this node_; ~0u if it doesn't correspond to any.
uint32_t context_index;
- // This is the schema for this node_; ~0u if it doesn't correspond to any.
- uint32_t schema_index;
+ // This is the type for this node_; ~0u if it doesn't correspond to any.
+ uint32_t type_index;
};
struct TrieNodeInternal {
@@ -61,7 +61,7 @@
uint32_t minimum_supported_version;
uint32_t size;
uint32_t contexts_offset;
- uint32_t schemas_offset;
+ uint32_t types_offset;
uint32_t root_offset;
};
@@ -103,7 +103,7 @@
}
uint32_t context_index() const { return node_property_entry()->context_index; }
- uint32_t schema_index() const { return node_property_entry()->schema_index; }
+ uint32_t type_index() const { return node_property_entry()->type_index; }
uint32_t num_child_nodes() const { return trie_node_base_->num_child_nodes; }
TrieNode child_node(int n) const {
@@ -143,12 +143,11 @@
class PropertyInfoArea : private SerializedData {
public:
- void GetPropertyInfoIndexes(const char* name, uint32_t* context_index,
- uint32_t* schema_index) const;
- void GetPropertyInfo(const char* property, const char** context, const char** schema) const;
+ void GetPropertyInfoIndexes(const char* name, uint32_t* context_index, uint32_t* type_index) const;
+ void GetPropertyInfo(const char* property, const char** context, const char** type) const;
int FindContextIndex(const char* context) const;
- int FindSchemaIndex(const char* schema) const;
+ int FindTypeIndex(const char* type) const;
const char* context(uint32_t index) const {
uint32_t context_array_size_offset = contexts_offset();
@@ -156,10 +155,10 @@
return data_base() + context_array[index];
}
- const char* schema(uint32_t index) const {
- uint32_t schema_array_size_offset = schemas_offset();
- const uint32_t* schema_array = uint32_array(schema_array_size_offset + sizeof(uint32_t));
- return data_base() + schema_array[index];
+ const char* type(uint32_t index) const {
+ uint32_t type_array_size_offset = types_offset();
+ const uint32_t* type_array = uint32_array(type_array_size_offset + sizeof(uint32_t));
+ return data_base() + type_array[index];
}
uint32_t current_version() const { return header()->current_version; }
@@ -168,21 +167,21 @@
uint32_t size() const { return SerializedData::size(); }
uint32_t num_contexts() const { return uint32_array(contexts_offset())[0]; }
- uint32_t num_schemas() const { return uint32_array(schemas_offset())[0]; }
+ uint32_t num_types() const { return uint32_array(types_offset())[0]; }
TrieNode root_node() const { return trie(header()->root_offset); }
private:
void CheckPrefixMatch(const char* remaining_name, const TrieNode& trie_node,
- uint32_t* context_index, uint32_t* schema_index) const;
+ uint32_t* context_index, uint32_t* type_index) const;
const PropertyInfoAreaHeader* header() const {
return reinterpret_cast<const PropertyInfoAreaHeader*>(data_base());
}
uint32_t contexts_offset() const { return header()->contexts_offset; }
uint32_t contexts_array_offset() const { return contexts_offset() + sizeof(uint32_t); }
- uint32_t schemas_offset() const { return header()->schemas_offset; }
- uint32_t schemas_array_offset() const { return schemas_offset() + sizeof(uint32_t); }
+ uint32_t types_offset() const { return header()->types_offset; }
+ uint32_t types_array_offset() const { return types_offset() + sizeof(uint32_t); }
TrieNode trie(uint32_t offset) const {
if (offset != 0 && offset > size()) return TrieNode();
diff --git a/property_service/libpropertyinfoparser/property_info_parser.cpp b/property_service/libpropertyinfoparser/property_info_parser.cpp
index a8f6636..489d81a 100644
--- a/property_service/libpropertyinfoparser/property_info_parser.cpp
+++ b/property_service/libpropertyinfoparser/property_info_parser.cpp
@@ -56,12 +56,12 @@
});
}
-// Binary search the list of schemas to find the index of a given schema string.
+// Binary search the list of types to find the index of a given type string.
// Only should be used for TrieSerializer to construct the Trie.
-int PropertyInfoArea::FindSchemaIndex(const char* schema) const {
- return Find(num_schemas(), [this, schema](auto array_offset) {
- auto string_offset = uint32_array(schemas_array_offset())[array_offset];
- return strcmp(c_string(string_offset), schema);
+int PropertyInfoArea::FindTypeIndex(const char* type) const {
+ return Find(num_types(), [this, type](auto array_offset) {
+ auto string_offset = uint32_array(types_array_offset())[array_offset];
+ return strcmp(c_string(string_offset), type);
});
}
@@ -89,7 +89,7 @@
}
void PropertyInfoArea::CheckPrefixMatch(const char* remaining_name, const TrieNode& trie_node,
- uint32_t* context_index, uint32_t* schema_index) const {
+ uint32_t* context_index, uint32_t* type_index) const {
const uint32_t remaining_name_size = strlen(remaining_name);
for (uint32_t i = 0; i < trie_node.num_prefixes(); ++i) {
auto prefix_len = trie_node.prefix(i)->namelen;
@@ -99,8 +99,8 @@
if (trie_node.prefix(i)->context_index != ~0u) {
*context_index = trie_node.prefix(i)->context_index;
}
- if (trie_node.prefix(i)->schema_index != ~0u) {
- *schema_index = trie_node.prefix(i)->schema_index;
+ if (trie_node.prefix(i)->type_index != ~0u) {
+ *type_index = trie_node.prefix(i)->type_index;
}
return;
}
@@ -108,9 +108,9 @@
}
void PropertyInfoArea::GetPropertyInfoIndexes(const char* name, uint32_t* context_index,
- uint32_t* schema_index) const {
+ uint32_t* type_index) const {
uint32_t return_context_index = ~0u;
- uint32_t return_schema_index = ~0u;
+ uint32_t return_type_index = ~0u;
const char* remaining_name = name;
auto trie_node = root_node();
while (true) {
@@ -120,13 +120,13 @@
if (trie_node.context_index() != ~0u) {
return_context_index = trie_node.context_index();
}
- if (trie_node.schema_index() != ~0u) {
- return_schema_index = trie_node.schema_index();
+ if (trie_node.type_index() != ~0u) {
+ return_type_index = trie_node.type_index();
}
// Check prefixes at this node. This comes after the node check since these prefixes are by
// definition longer than the node itself.
- CheckPrefixMatch(remaining_name, trie_node, &return_context_index, &return_schema_index);
+ CheckPrefixMatch(remaining_name, trie_node, &return_context_index, &return_type_index);
if (sep == nullptr) {
break;
@@ -153,29 +153,29 @@
*context_index = return_context_index;
}
}
- if (schema_index != nullptr) {
- if (trie_node.exact_match(i)->schema_index != ~0u) {
- *schema_index = trie_node.exact_match(i)->schema_index;
+ if (type_index != nullptr) {
+ if (trie_node.exact_match(i)->type_index != ~0u) {
+ *type_index = trie_node.exact_match(i)->type_index;
} else {
- *schema_index = return_schema_index;
+ *type_index = return_type_index;
}
}
return;
}
}
// Check prefix matches for prefixes not deliminated with '.'
- CheckPrefixMatch(remaining_name, trie_node, &return_context_index, &return_schema_index);
+ CheckPrefixMatch(remaining_name, trie_node, &return_context_index, &return_type_index);
// Return previously found prefix match.
if (context_index != nullptr) *context_index = return_context_index;
- if (schema_index != nullptr) *schema_index = return_schema_index;
+ if (type_index != nullptr) *type_index = return_type_index;
return;
}
void PropertyInfoArea::GetPropertyInfo(const char* property, const char** context,
- const char** schema) const {
+ const char** type) const {
uint32_t context_index;
- uint32_t schema_index;
- GetPropertyInfoIndexes(property, &context_index, &schema_index);
+ uint32_t type_index;
+ GetPropertyInfoIndexes(property, &context_index, &type_index);
if (context != nullptr) {
if (context_index == ~0u) {
*context = nullptr;
@@ -183,11 +183,11 @@
*context = this->context(context_index);
}
}
- if (schema != nullptr) {
- if (schema_index == ~0u) {
- *schema = nullptr;
+ if (type != nullptr) {
+ if (type_index == ~0u) {
+ *type = nullptr;
} else {
- *schema = this->schema(schema_index);
+ *type = this->type(type_index);
}
}
}
diff --git a/property_service/libpropertyinfoserializer/Android.bp b/property_service/libpropertyinfoserializer/Android.bp
index be177f9..72ae19a 100644
--- a/property_service/libpropertyinfoserializer/Android.bp
+++ b/property_service/libpropertyinfoserializer/Android.bp
@@ -1,6 +1,7 @@
cc_defaults {
name: "propertyinfoserializer_defaults",
host_supported: true,
+ vendor_available: true,
cpp_std: "experimental",
cppflags: [
"-Wall",
diff --git a/property_service/libpropertyinfoserializer/include/property_info_serializer/property_info_serializer.h b/property_service/libpropertyinfoserializer/include/property_info_serializer/property_info_serializer.h
index d2ec385..439813d 100644
--- a/property_service/libpropertyinfoserializer/include/property_info_serializer/property_info_serializer.h
+++ b/property_service/libpropertyinfoserializer/include/property_info_serializer/property_info_serializer.h
@@ -26,19 +26,19 @@
struct PropertyInfoEntry {
PropertyInfoEntry() {}
template <typename T, typename U, typename V>
- PropertyInfoEntry(T&& name, U&& context, V&& schema, bool exact_match)
+ PropertyInfoEntry(T&& name, U&& context, V&& type, bool exact_match)
: name(std::forward<T>(name)),
context(std::forward<U>(context)),
- schema(std::forward<V>(schema)),
+ type(std::forward<V>(type)),
exact_match(exact_match) {}
std::string name;
std::string context;
- std::string schema;
+ std::string type;
bool exact_match;
};
bool BuildTrie(const std::vector<PropertyInfoEntry>& property_info,
- const std::string& default_context, const std::string& default_schema,
+ const std::string& default_context, const std::string& default_type,
std::string* serialized_trie, std::string* error);
void ParsePropertyInfoFile(const std::string& file_contents,
diff --git a/property_service/libpropertyinfoserializer/property_info_file.cpp b/property_service/libpropertyinfoserializer/property_info_file.cpp
index 702f219..2cdc62d 100644
--- a/property_service/libpropertyinfoserializer/property_info_file.cpp
+++ b/property_service/libpropertyinfoserializer/property_info_file.cpp
@@ -1,9 +1,26 @@
+//
+// Copyright (C) 2017 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 <property_info_serializer/property_info_serializer.h>
#include <android-base/strings.h>
#include "space_tokenizer.h"
+using android::base::Join;
using android::base::Split;
using android::base::StartsWith;
using android::base::Trim;
@@ -11,6 +28,34 @@
namespace android {
namespace properties {
+namespace {
+
+bool IsTypeValid(const std::vector<std::string>& type_strings) {
+ if (type_strings.empty()) {
+ return false;
+ }
+
+ // There must be at least one string following 'enum'
+ if (type_strings[0] == "enum") {
+ return type_strings.size() > 1;
+ }
+
+ // There should not be any string following any other types.
+ if (type_strings.size() != 1) {
+ return false;
+ }
+
+ // Check that the type matches one of remaining valid types.
+ static const char* const no_parameter_types[] = {"string", "bool", "int",
+ "uint", "double", "size"};
+ for (const auto& type : no_parameter_types) {
+ if (type_strings[0] == type) {
+ return true;
+ }
+ }
+ return false;
+}
+
bool ParsePropertyInfoLine(const std::string& line, PropertyInfoEntry* out, std::string* error) {
auto tokenizer = SpaceTokenizer(line);
@@ -26,14 +71,28 @@
return false;
}
- // It is not an error to not find these, as older files will not contain them.
+ // It is not an error to not find exact_match or a type, as older files will not contain them.
auto exact_match = tokenizer.GetNext();
- auto schema = tokenizer.GetRemaining();
+ // We reformat type to be space deliminated regardless of the input whitespace for easier storage
+ // and subsequent parsing.
+ auto type_strings = std::vector<std::string>{};
+ auto type = tokenizer.GetNext();
+ while (!type.empty()) {
+ type_strings.emplace_back(type);
+ type = tokenizer.GetNext();
+ }
- *out = {property, context, schema, exact_match == "exact"};
+ if (!type_strings.empty() && !IsTypeValid(type_strings)) {
+ *error = "Type '" + Join(type_strings, " ") + "' is not valid";
+ return false;
+ }
+
+ *out = {property, context, Join(type_strings, " "), exact_match == "exact"};
return true;
}
+} // namespace
+
void ParsePropertyInfoFile(const std::string& file_contents,
std::vector<PropertyInfoEntry>* property_infos,
std::vector<std::string>* errors) {
diff --git a/property_service/libpropertyinfoserializer/property_info_serializer.cpp b/property_service/libpropertyinfoserializer/property_info_serializer.cpp
index 656c96e..803657a 100644
--- a/property_service/libpropertyinfoserializer/property_info_serializer.cpp
+++ b/property_service/libpropertyinfoserializer/property_info_serializer.cpp
@@ -27,13 +27,13 @@
namespace properties {
bool BuildTrie(const std::vector<PropertyInfoEntry>& property_info,
- const std::string& default_context, const std::string& default_schema,
+ const std::string& default_context, const std::string& default_type,
std::string* serialized_trie, std::string* error) {
// Check that names are legal first
- auto trie_builder = TrieBuilder(default_context, default_schema);
+ auto trie_builder = TrieBuilder(default_context, default_type);
- for (const auto& [name, context, schema, is_exact] : property_info) {
- if (!trie_builder.AddToTrie(name, context, schema, is_exact, error)) {
+ for (const auto& [name, context, type, is_exact] : property_info) {
+ if (!trie_builder.AddToTrie(name, context, type, is_exact, error)) {
return false;
}
}
diff --git a/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp b/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp
index 46c2d06..f484550 100644
--- a/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp
+++ b/property_service/libpropertyinfoserializer/property_info_serializer_test.cpp
@@ -46,7 +46,7 @@
auto root_node = property_info_area->root_node();
EXPECT_STREQ("root", root_node.name());
EXPECT_STREQ("default", property_info_area->context(root_node.context_index()));
- EXPECT_STREQ("default", property_info_area->schema(root_node.schema_index()));
+ EXPECT_STREQ("default", property_info_area->type(root_node.type_index()));
EXPECT_EQ(0U, root_node.num_prefixes());
EXPECT_EQ(0U, root_node.num_exact_matches());
@@ -59,7 +59,7 @@
EXPECT_STREQ("test", test_node.name());
EXPECT_STREQ("1st", property_info_area->context(test_node.context_index()));
- EXPECT_STREQ("1st", property_info_area->schema(test_node.schema_index()));
+ EXPECT_STREQ("1st", property_info_area->type(test_node.type_index()));
EXPECT_EQ(0U, test_node.num_child_nodes());
@@ -69,7 +69,7 @@
EXPECT_STREQ("test", serialized_trie.data() + prefix->name_offset);
EXPECT_EQ(4U, prefix->namelen);
EXPECT_STREQ("2nd", property_info_area->context(prefix->context_index));
- EXPECT_STREQ("2nd", property_info_area->schema(prefix->schema_index));
+ EXPECT_STREQ("2nd", property_info_area->type(prefix->type_index));
}
EXPECT_EQ(3U, test_node.num_exact_matches());
@@ -85,9 +85,9 @@
EXPECT_STREQ("3rd", property_info_area->context(match2->context_index));
EXPECT_STREQ("3rd", property_info_area->context(match3->context_index));
- EXPECT_STREQ("3rd", property_info_area->schema(match1->schema_index));
- EXPECT_STREQ("3rd", property_info_area->schema(match2->schema_index));
- EXPECT_STREQ("3rd", property_info_area->schema(match3->schema_index));
+ EXPECT_STREQ("3rd", property_info_area->type(match1->type_index));
+ EXPECT_STREQ("3rd", property_info_area->type(match2->type_index));
+ EXPECT_STREQ("3rd", property_info_area->type(match3->type_index));
}
// Check the long string node
@@ -120,7 +120,7 @@
auto final_match = long_string_node.exact_match(0);
EXPECT_STREQ("string", serialized_trie.data() + final_match->name_offset);
EXPECT_STREQ("4th", property_info_area->context(final_match->context_index));
- EXPECT_STREQ("4th", property_info_area->schema(final_match->schema_index));
+ EXPECT_STREQ("4th", property_info_area->type(final_match->type_index));
}
TEST(propertyinfoserializer, GetPropertyInfo) {
@@ -143,109 +143,109 @@
auto root_node = property_info_area->root_node();
EXPECT_STREQ("root", root_node.name());
EXPECT_STREQ("default", property_info_area->context(root_node.context_index()));
- EXPECT_STREQ("default", property_info_area->schema(root_node.schema_index()));
+ EXPECT_STREQ("default", property_info_area->type(root_node.type_index()));
const char* context;
- const char* schema;
- property_info_area->GetPropertyInfo("abc", &context, &schema);
+ const char* type;
+ property_info_area->GetPropertyInfo("abc", &context, &type);
EXPECT_STREQ("default", context);
- EXPECT_STREQ("default", schema);
- property_info_area->GetPropertyInfo("abc.abc", &context, &schema);
+ EXPECT_STREQ("default", type);
+ property_info_area->GetPropertyInfo("abc.abc", &context, &type);
EXPECT_STREQ("default", context);
- EXPECT_STREQ("default", schema);
- property_info_area->GetPropertyInfo("123.abc", &context, &schema);
+ EXPECT_STREQ("default", type);
+ property_info_area->GetPropertyInfo("123.abc", &context, &type);
EXPECT_STREQ("default", context);
- EXPECT_STREQ("default", schema);
+ EXPECT_STREQ("default", type);
- property_info_area->GetPropertyInfo("test.a", &context, &schema);
+ property_info_area->GetPropertyInfo("test.a", &context, &type);
EXPECT_STREQ("1st", context);
- EXPECT_STREQ("1st", schema);
- property_info_area->GetPropertyInfo("test.b", &context, &schema);
+ EXPECT_STREQ("1st", type);
+ property_info_area->GetPropertyInfo("test.b", &context, &type);
EXPECT_STREQ("1st", context);
- EXPECT_STREQ("1st", schema);
- property_info_area->GetPropertyInfo("test.c", &context, &schema);
+ EXPECT_STREQ("1st", type);
+ property_info_area->GetPropertyInfo("test.c", &context, &type);
EXPECT_STREQ("1st", context);
- EXPECT_STREQ("1st", schema);
+ EXPECT_STREQ("1st", type);
- property_info_area->GetPropertyInfo("test.test", &context, &schema);
+ property_info_area->GetPropertyInfo("test.test", &context, &type);
EXPECT_STREQ("5th", context);
- EXPECT_STREQ("5th", schema);
- property_info_area->GetPropertyInfo("test.testa", &context, &schema);
+ EXPECT_STREQ("5th", type);
+ property_info_area->GetPropertyInfo("test.testa", &context, &type);
EXPECT_STREQ("2nd", context);
- EXPECT_STREQ("2nd", schema);
- property_info_area->GetPropertyInfo("test.testb", &context, &schema);
+ EXPECT_STREQ("2nd", type);
+ property_info_area->GetPropertyInfo("test.testb", &context, &type);
EXPECT_STREQ("2nd", context);
- EXPECT_STREQ("2nd", schema);
- property_info_area->GetPropertyInfo("test.testc", &context, &schema);
+ EXPECT_STREQ("2nd", type);
+ property_info_area->GetPropertyInfo("test.testc", &context, &type);
EXPECT_STREQ("2nd", context);
- EXPECT_STREQ("2nd", schema);
+ EXPECT_STREQ("2nd", type);
- property_info_area->GetPropertyInfo("test.test.a", &context, &schema);
+ property_info_area->GetPropertyInfo("test.test.a", &context, &type);
EXPECT_STREQ("2nd", context);
- EXPECT_STREQ("2nd", schema);
- property_info_area->GetPropertyInfo("test.test.b", &context, &schema);
+ EXPECT_STREQ("2nd", type);
+ property_info_area->GetPropertyInfo("test.test.b", &context, &type);
EXPECT_STREQ("2nd", context);
- EXPECT_STREQ("2nd", schema);
- property_info_area->GetPropertyInfo("test.test.c", &context, &schema);
+ EXPECT_STREQ("2nd", type);
+ property_info_area->GetPropertyInfo("test.test.c", &context, &type);
EXPECT_STREQ("2nd", context);
- EXPECT_STREQ("2nd", schema);
+ EXPECT_STREQ("2nd", type);
- property_info_area->GetPropertyInfo("test.test1", &context, &schema);
+ property_info_area->GetPropertyInfo("test.test1", &context, &type);
EXPECT_STREQ("3rd", context);
- EXPECT_STREQ("3rd", schema);
- property_info_area->GetPropertyInfo("test.test2", &context, &schema);
+ EXPECT_STREQ("3rd", type);
+ property_info_area->GetPropertyInfo("test.test2", &context, &type);
EXPECT_STREQ("7th", context);
- EXPECT_STREQ("7th", schema);
- property_info_area->GetPropertyInfo("test.test3", &context, &schema);
+ EXPECT_STREQ("7th", type);
+ property_info_area->GetPropertyInfo("test.test3", &context, &type);
EXPECT_STREQ("3rd", context);
- EXPECT_STREQ("3rd", schema);
+ EXPECT_STREQ("3rd", type);
- property_info_area->GetPropertyInfo("test.test11", &context, &schema);
+ property_info_area->GetPropertyInfo("test.test11", &context, &type);
EXPECT_STREQ("2nd", context);
- EXPECT_STREQ("2nd", schema);
- property_info_area->GetPropertyInfo("test.test22", &context, &schema);
+ EXPECT_STREQ("2nd", type);
+ property_info_area->GetPropertyInfo("test.test22", &context, &type);
EXPECT_STREQ("2nd", context);
- EXPECT_STREQ("2nd", schema);
- property_info_area->GetPropertyInfo("test.test33", &context, &schema);
+ EXPECT_STREQ("2nd", type);
+ property_info_area->GetPropertyInfo("test.test33", &context, &type);
EXPECT_STREQ("2nd", context);
- EXPECT_STREQ("2nd", schema);
+ EXPECT_STREQ("2nd", type);
- property_info_area->GetPropertyInfo("this.is.a.long.string", &context, &schema);
+ property_info_area->GetPropertyInfo("this.is.a.long.string", &context, &type);
EXPECT_STREQ("4th", context);
- EXPECT_STREQ("4th", schema);
+ EXPECT_STREQ("4th", type);
- property_info_area->GetPropertyInfo("this.is.a.long", &context, &schema);
+ property_info_area->GetPropertyInfo("this.is.a.long", &context, &type);
EXPECT_STREQ("default", context);
- EXPECT_STREQ("default", schema);
- property_info_area->GetPropertyInfo("this.is.a", &context, &schema);
+ EXPECT_STREQ("default", type);
+ property_info_area->GetPropertyInfo("this.is.a", &context, &type);
EXPECT_STREQ("default", context);
- EXPECT_STREQ("default", schema);
- property_info_area->GetPropertyInfo("this.is", &context, &schema);
+ EXPECT_STREQ("default", type);
+ property_info_area->GetPropertyInfo("this.is", &context, &type);
EXPECT_STREQ("default", context);
- EXPECT_STREQ("default", schema);
- property_info_area->GetPropertyInfo("this", &context, &schema);
+ EXPECT_STREQ("default", type);
+ property_info_area->GetPropertyInfo("this", &context, &type);
EXPECT_STREQ("default", context);
- EXPECT_STREQ("default", schema);
+ EXPECT_STREQ("default", type);
- property_info_area->GetPropertyInfo("test.test2.a", &context, &schema);
+ property_info_area->GetPropertyInfo("test.test2.a", &context, &type);
EXPECT_STREQ("6th", context);
- EXPECT_STREQ("6th", schema);
+ EXPECT_STREQ("6th", type);
- property_info_area->GetPropertyInfo("testoneword", &context, &schema);
+ property_info_area->GetPropertyInfo("testoneword", &context, &type);
EXPECT_STREQ("8th", context);
- EXPECT_STREQ("8th", schema);
+ EXPECT_STREQ("8th", type);
- property_info_area->GetPropertyInfo("testwordprefix", &context, &schema);
+ property_info_area->GetPropertyInfo("testwordprefix", &context, &type);
EXPECT_STREQ("9th", context);
- EXPECT_STREQ("9th", schema);
+ EXPECT_STREQ("9th", type);
- property_info_area->GetPropertyInfo("testwordprefixblah", &context, &schema);
+ property_info_area->GetPropertyInfo("testwordprefixblah", &context, &type);
EXPECT_STREQ("9th", context);
- EXPECT_STREQ("9th", schema);
+ EXPECT_STREQ("9th", type);
- property_info_area->GetPropertyInfo("testwordprefix.blah", &context, &schema);
+ property_info_area->GetPropertyInfo("testwordprefix.blah", &context, &type);
EXPECT_STREQ("9th", context);
- EXPECT_STREQ("9th", schema);
+ EXPECT_STREQ("9th", type);
}
TEST(propertyinfoserializer, RealProperties) {
@@ -777,35 +777,34 @@
auto property_info_area = reinterpret_cast<const PropertyInfoArea*>(serialized_trie.data());
const char* context;
- const char* schema;
- property_info_area->GetPropertyInfo("persist.radio", &context, &schema);
+ const char* type;
+ property_info_area->GetPropertyInfo("persist.radio", &context, &type);
EXPECT_STREQ("1st", context);
- EXPECT_STREQ("1st", schema);
- property_info_area->GetPropertyInfo("persist.radio.subproperty", &context, &schema);
+ EXPECT_STREQ("1st", type);
+ property_info_area->GetPropertyInfo("persist.radio.subproperty", &context, &type);
EXPECT_STREQ("1st", context);
- EXPECT_STREQ("1st", schema);
- property_info_area->GetPropertyInfo("persist.radiowords", &context, &schema);
+ EXPECT_STREQ("1st", type);
+ property_info_area->GetPropertyInfo("persist.radiowords", &context, &type);
EXPECT_STREQ("1st", context);
- EXPECT_STREQ("1st", schema);
- property_info_area->GetPropertyInfo("persist.radio.long.long.long.sub.property", &context,
- &schema);
+ EXPECT_STREQ("1st", type);
+ property_info_area->GetPropertyInfo("persist.radio.long.long.long.sub.property", &context, &type);
EXPECT_STREQ("1st", context);
- EXPECT_STREQ("1st", schema);
- property_info_area->GetPropertyInfo("persist.radio.something.else.here", &context, &schema);
+ EXPECT_STREQ("1st", type);
+ property_info_area->GetPropertyInfo("persist.radio.something.else.here", &context, &type);
EXPECT_STREQ("2nd", context);
- EXPECT_STREQ("2nd", schema);
- property_info_area->GetPropertyInfo("persist.radio.something.else.here2", &context, &schema);
+ EXPECT_STREQ("2nd", type);
+ property_info_area->GetPropertyInfo("persist.radio.something.else.here2", &context, &type);
EXPECT_STREQ("2nd", context);
- EXPECT_STREQ("2nd", schema);
- property_info_area->GetPropertyInfo("persist.radio.something.else.here.after", &context, &schema);
+ EXPECT_STREQ("2nd", type);
+ property_info_area->GetPropertyInfo("persist.radio.something.else.here.after", &context, &type);
EXPECT_STREQ("2nd", context);
- EXPECT_STREQ("2nd", schema);
- property_info_area->GetPropertyInfo("persist.radio.something.else.nothere", &context, &schema);
+ EXPECT_STREQ("2nd", type);
+ property_info_area->GetPropertyInfo("persist.radio.something.else.nothere", &context, &type);
EXPECT_STREQ("1st", context);
- EXPECT_STREQ("1st", schema);
- property_info_area->GetPropertyInfo("persist.radio.something.else", &context, &schema);
+ EXPECT_STREQ("1st", type);
+ property_info_area->GetPropertyInfo("persist.radio.something.else", &context, &type);
EXPECT_STREQ("1st", context);
- EXPECT_STREQ("1st", schema);
+ EXPECT_STREQ("1st", type);
}
TEST(propertyinfoserializer, GetPropertyInfo_prefix_with_dot_vs_without) {
@@ -823,28 +822,28 @@
auto property_info_area = reinterpret_cast<const PropertyInfoArea*>(serialized_trie.data());
const char* context;
- const char* schema;
- property_info_area->GetPropertyInfo("persist.notradio", &context, &schema);
+ const char* type;
+ property_info_area->GetPropertyInfo("persist.notradio", &context, &type);
EXPECT_STREQ("1st", context);
- EXPECT_STREQ("1st", schema);
- property_info_area->GetPropertyInfo("persist.radio", &context, &schema);
+ EXPECT_STREQ("1st", type);
+ property_info_area->GetPropertyInfo("persist.radio", &context, &type);
EXPECT_STREQ("2nd", context);
- EXPECT_STREQ("2nd", schema);
- property_info_area->GetPropertyInfo("persist.radio.subproperty", &context, &schema);
+ EXPECT_STREQ("2nd", type);
+ property_info_area->GetPropertyInfo("persist.radio.subproperty", &context, &type);
EXPECT_STREQ("2nd", context);
- EXPECT_STREQ("2nd", schema);
- property_info_area->GetPropertyInfo("persist.radiowords", &context, &schema);
+ EXPECT_STREQ("2nd", type);
+ property_info_area->GetPropertyInfo("persist.radiowords", &context, &type);
EXPECT_STREQ("2nd", context);
- EXPECT_STREQ("2nd", schema);
- property_info_area->GetPropertyInfo("persist.radio.long.property.prefix.match", &context, &schema);
+ EXPECT_STREQ("2nd", type);
+ property_info_area->GetPropertyInfo("persist.radio.long.property.prefix.match", &context, &type);
EXPECT_STREQ("2nd", context);
- EXPECT_STREQ("2nd", schema);
- property_info_area->GetPropertyInfo("persist.radio.long.property.exact.match", &context, &schema);
+ EXPECT_STREQ("2nd", type);
+ property_info_area->GetPropertyInfo("persist.radio.long.property.exact.match", &context, &type);
EXPECT_STREQ("3rd", context);
- EXPECT_STREQ("3rd", schema);
+ EXPECT_STREQ("3rd", type);
}
-TEST(propertyinfoserializer, GetPropertyInfo_empty_context_and_schema) {
+TEST(propertyinfoserializer, GetPropertyInfo_empty_context_and_type) {
auto property_info = std::vector<PropertyInfoEntry>{
{"persist.", "1st", "", false},
{"persist.dot_prefix.", "2nd", "", false},
@@ -862,28 +861,28 @@
auto property_info_area = reinterpret_cast<const PropertyInfoArea*>(serialized_trie.data());
const char* context;
- const char* schema;
- property_info_area->GetPropertyInfo("notpersist.radio.something", &context, &schema);
+ const char* type;
+ property_info_area->GetPropertyInfo("notpersist.radio.something", &context, &type);
EXPECT_STREQ("default", context);
- EXPECT_STREQ("default", schema);
- property_info_area->GetPropertyInfo("persist.nomatch", &context, &schema);
+ EXPECT_STREQ("default", type);
+ property_info_area->GetPropertyInfo("persist.nomatch", &context, &type);
EXPECT_STREQ("1st", context);
- EXPECT_STREQ("default", schema);
- property_info_area->GetPropertyInfo("persist.dot_prefix.something", &context, &schema);
+ EXPECT_STREQ("default", type);
+ property_info_area->GetPropertyInfo("persist.dot_prefix.something", &context, &type);
EXPECT_STREQ("2nd", context);
- EXPECT_STREQ("default", schema);
- property_info_area->GetPropertyInfo("persist.non_dot_prefix.something", &context, &schema);
+ EXPECT_STREQ("default", type);
+ property_info_area->GetPropertyInfo("persist.non_dot_prefix.something", &context, &type);
EXPECT_STREQ("3rd", context);
- EXPECT_STREQ("default", schema);
- property_info_area->GetPropertyInfo("persist.exact_match", &context, &schema);
+ EXPECT_STREQ("default", type);
+ property_info_area->GetPropertyInfo("persist.exact_match", &context, &type);
EXPECT_STREQ("1st", context);
- EXPECT_STREQ("default", schema);
- property_info_area->GetPropertyInfo("persist.dot_prefix2.something", &context, &schema);
+ EXPECT_STREQ("default", type);
+ property_info_area->GetPropertyInfo("persist.dot_prefix2.something", &context, &type);
EXPECT_STREQ("1st", context);
- EXPECT_STREQ("4th", schema);
- property_info_area->GetPropertyInfo("persist.non_dot_prefix2.something", &context, &schema);
+ EXPECT_STREQ("4th", type);
+ property_info_area->GetPropertyInfo("persist.non_dot_prefix2.something", &context, &type);
EXPECT_STREQ("1st", context);
- EXPECT_STREQ("5th", schema);
+ EXPECT_STREQ("5th", type);
}
} // namespace properties
diff --git a/property_service/libpropertyinfoserializer/trie_builder.cpp b/property_service/libpropertyinfoserializer/trie_builder.cpp
index feb753b..8c5ce84 100644
--- a/property_service/libpropertyinfoserializer/trie_builder.cpp
+++ b/property_service/libpropertyinfoserializer/trie_builder.cpp
@@ -23,23 +23,23 @@
namespace android {
namespace properties {
-TrieBuilder::TrieBuilder(const std::string& default_context, const std::string& default_schema)
+TrieBuilder::TrieBuilder(const std::string& default_context, const std::string& default_type)
: builder_root_("root") {
auto* context_pointer = StringPointerFromContainer(default_context, &contexts_);
builder_root_.set_context(context_pointer);
- auto* schema_pointer = StringPointerFromContainer(default_schema, &schemas_);
- builder_root_.set_schema(schema_pointer);
+ auto* type_pointer = StringPointerFromContainer(default_type, &types_);
+ builder_root_.set_type(type_pointer);
}
bool TrieBuilder::AddToTrie(const std::string& name, const std::string& context,
- const std::string& schema, bool exact, std::string* error) {
+ const std::string& type, bool exact, std::string* error) {
auto* context_pointer = StringPointerFromContainer(context, &contexts_);
- auto* schema_pointer = StringPointerFromContainer(schema, &schemas_);
- return AddToTrie(name, context_pointer, schema_pointer, exact, error);
+ auto* type_pointer = StringPointerFromContainer(type, &types_);
+ return AddToTrie(name, context_pointer, type_pointer, exact, error);
}
bool TrieBuilder::AddToTrie(const std::string& name, const std::string* context,
- const std::string* schema, bool exact, std::string* error) {
+ const std::string* type, bool exact, std::string* error) {
TrieBuilderNode* current_node = &builder_root_;
auto name_pieces = Split(name, ".");
@@ -66,12 +66,12 @@
// Store our context based on what type of match it is.
if (exact) {
- if (!current_node->AddExactMatchContext(name_pieces.front(), context, schema)) {
+ if (!current_node->AddExactMatchContext(name_pieces.front(), context, type)) {
*error = "Duplicate exact match detected for '" + name + "'";
return false;
}
} else if (!ends_with_dot) {
- if (!current_node->AddPrefixContext(name_pieces.front(), context, schema)) {
+ if (!current_node->AddPrefixContext(name_pieces.front(), context, type)) {
*error = "Duplicate prefix match detected for '" + name + "'";
return false;
}
@@ -84,12 +84,12 @@
*error = "Unable to allocate Trie node";
return false;
}
- if (child->context() != nullptr || child->schema() != nullptr) {
+ if (child->context() != nullptr || child->type() != nullptr) {
*error = "Duplicate prefix match detected for '" + name + "'";
return false;
}
child->set_context(context);
- child->set_schema(schema);
+ child->set_type(type);
}
return true;
}
diff --git a/property_service/libpropertyinfoserializer/trie_builder.h b/property_service/libpropertyinfoserializer/trie_builder.h
index f928e76..b971589 100644
--- a/property_service/libpropertyinfoserializer/trie_builder.h
+++ b/property_service/libpropertyinfoserializer/trie_builder.h
@@ -26,13 +26,12 @@
namespace properties {
struct PropertyEntryBuilder {
- PropertyEntryBuilder() : context(nullptr), schema(nullptr) {}
- PropertyEntryBuilder(const std::string& name, const std::string* context,
- const std::string* schema)
- : name(name), context(context), schema(schema) {}
+ PropertyEntryBuilder() : context(nullptr), type(nullptr) {}
+ PropertyEntryBuilder(const std::string& name, const std::string* context, const std::string* type)
+ : name(name), context(context), type(type) {}
std::string name;
const std::string* context;
- const std::string* schema;
+ const std::string* type;
};
class TrieBuilderNode {
@@ -56,33 +55,33 @@
TrieBuilderNode* AddChild(const std::string& name) { return &children_.emplace_back(name); }
bool AddPrefixContext(const std::string& prefix, const std::string* context,
- const std::string* schema) {
+ const std::string* type) {
if (std::find_if(prefixes_.begin(), prefixes_.end(),
[&prefix](const auto& t) { return t.name == prefix; }) != prefixes_.end()) {
return false;
}
- prefixes_.emplace_back(prefix, context, schema);
+ prefixes_.emplace_back(prefix, context, type);
return true;
}
bool AddExactMatchContext(const std::string& exact_match, const std::string* context,
- const std::string* schema) {
+ const std::string* type) {
if (std::find_if(exact_matches_.begin(), exact_matches_.end(), [&exact_match](const auto& t) {
return t.name == exact_match;
}) != exact_matches_.end()) {
return false;
}
- exact_matches_.emplace_back(exact_match, context, schema);
+ exact_matches_.emplace_back(exact_match, context, type);
return true;
}
const std::string& name() const { return property_entry_.name; }
const std::string* context() const { return property_entry_.context; }
void set_context(const std::string* context) { property_entry_.context = context; }
- const std::string* schema() const { return property_entry_.schema; }
- void set_schema(const std::string* schema) { property_entry_.schema = schema; }
+ const std::string* type() const { return property_entry_.type; }
+ void set_type(const std::string* type) { property_entry_.type = type; }
const PropertyEntryBuilder property_entry() const { return property_entry_; }
@@ -99,23 +98,23 @@
class TrieBuilder {
public:
- TrieBuilder(const std::string& default_context, const std::string& default_schema);
- bool AddToTrie(const std::string& name, const std::string& context, const std::string& schema,
+ TrieBuilder(const std::string& default_context, const std::string& default_type);
+ bool AddToTrie(const std::string& name, const std::string& context, const std::string& type,
bool exact, std::string* error);
const TrieBuilderNode builder_root() const { return builder_root_; }
const std::set<std::string>& contexts() const { return contexts_; }
- const std::set<std::string>& schemas() const { return schemas_; }
+ const std::set<std::string>& types() const { return types_; }
private:
- bool AddToTrie(const std::string& name, const std::string* context, const std::string* schema,
+ bool AddToTrie(const std::string& name, const std::string* context, const std::string* type,
bool exact, std::string* error);
const std::string* StringPointerFromContainer(const std::string& string,
std::set<std::string>* container);
TrieBuilderNode builder_root_;
std::set<std::string> contexts_;
- std::set<std::string> schemas_;
+ std::set<std::string> types_;
};
} // namespace properties
diff --git a/property_service/libpropertyinfoserializer/trie_builder_test.cpp b/property_service/libpropertyinfoserializer/trie_builder_test.cpp
index 2b948f3..5078810 100644
--- a/property_service/libpropertyinfoserializer/trie_builder_test.cpp
+++ b/property_service/libpropertyinfoserializer/trie_builder_test.cpp
@@ -22,19 +22,19 @@
namespace properties {
TEST(propertyinfoserializer, BuildTrie_Simple) {
- auto trie_builder = TrieBuilder("default", "default_schema");
+ auto trie_builder = TrieBuilder("default", "default_type");
// Add test data to tree
auto error = std::string();
- EXPECT_TRUE(trie_builder.AddToTrie("test.", "1st", "1st_schema", false, &error));
- EXPECT_TRUE(trie_builder.AddToTrie("test.test", "2nd", "2nd_schema", false, &error));
- EXPECT_TRUE(trie_builder.AddToTrie("test.test1", "3rd", "3rd_schema", true, &error));
- EXPECT_TRUE(trie_builder.AddToTrie("test.test2", "3rd", "3rd_schema", true, &error));
- EXPECT_TRUE(trie_builder.AddToTrie("test.test3", "3rd", "3rd_schema", true, &error));
- EXPECT_TRUE(trie_builder.AddToTrie("this.is.a.long.string", "4th", "4th_schema", true, &error));
+ EXPECT_TRUE(trie_builder.AddToTrie("test.", "1st", "1st_type", false, &error));
+ EXPECT_TRUE(trie_builder.AddToTrie("test.test", "2nd", "2nd_type", false, &error));
+ EXPECT_TRUE(trie_builder.AddToTrie("test.test1", "3rd", "3rd_type", true, &error));
+ EXPECT_TRUE(trie_builder.AddToTrie("test.test2", "3rd", "3rd_type", true, &error));
+ EXPECT_TRUE(trie_builder.AddToTrie("test.test3", "3rd", "3rd_type", true, &error));
+ EXPECT_TRUE(trie_builder.AddToTrie("this.is.a.long.string", "4th", "4th_type", true, &error));
ASSERT_EQ(5U, trie_builder.contexts().size());
- ASSERT_EQ(5U, trie_builder.schemas().size());
+ ASSERT_EQ(5U, trie_builder.types().size());
auto& builder_root = trie_builder.builder_root();
@@ -42,8 +42,8 @@
EXPECT_EQ("root", builder_root.name());
ASSERT_NE(nullptr, builder_root.context());
EXPECT_EQ("default", *builder_root.context());
- ASSERT_NE(nullptr, builder_root.schema());
- EXPECT_EQ("default_schema", *builder_root.schema());
+ ASSERT_NE(nullptr, builder_root.type());
+ EXPECT_EQ("default_type", *builder_root.type());
EXPECT_EQ(0U, builder_root.prefixes().size());
EXPECT_EQ(0U, builder_root.exact_matches().size());
@@ -55,8 +55,8 @@
EXPECT_EQ("test", test_node->name());
ASSERT_NE(nullptr, test_node->context());
EXPECT_EQ("1st", *test_node->context());
- ASSERT_NE(nullptr, test_node->schema());
- EXPECT_EQ("1st_schema", *test_node->schema());
+ ASSERT_NE(nullptr, test_node->type());
+ EXPECT_EQ("1st_type", *test_node->type());
EXPECT_EQ(0U, test_node->children().size());
EXPECT_EQ(1U, test_node->prefixes().size());
@@ -65,8 +65,8 @@
EXPECT_EQ("test", property_entry.name);
ASSERT_NE(nullptr, property_entry.context);
EXPECT_EQ("2nd", *property_entry.context);
- ASSERT_NE(nullptr, property_entry.schema);
- EXPECT_EQ("2nd_schema", *property_entry.schema);
+ ASSERT_NE(nullptr, property_entry.type);
+ EXPECT_EQ("2nd_type", *property_entry.type);
}
EXPECT_EQ(3U, test_node->exact_matches().size());
EXPECT_EQ("test1", test_node->exact_matches()[0].name);
@@ -80,18 +80,18 @@
EXPECT_EQ("3rd", *test_node->exact_matches()[1].context);
EXPECT_EQ("3rd", *test_node->exact_matches()[2].context);
- ASSERT_NE(nullptr, test_node->exact_matches()[0].schema);
- ASSERT_NE(nullptr, test_node->exact_matches()[1].schema);
- ASSERT_NE(nullptr, test_node->exact_matches()[2].schema);
- EXPECT_EQ("3rd_schema", *test_node->exact_matches()[0].schema);
- EXPECT_EQ("3rd_schema", *test_node->exact_matches()[1].schema);
- EXPECT_EQ("3rd_schema", *test_node->exact_matches()[2].schema);
+ ASSERT_NE(nullptr, test_node->exact_matches()[0].type);
+ ASSERT_NE(nullptr, test_node->exact_matches()[1].type);
+ ASSERT_NE(nullptr, test_node->exact_matches()[2].type);
+ EXPECT_EQ("3rd_type", *test_node->exact_matches()[0].type);
+ EXPECT_EQ("3rd_type", *test_node->exact_matches()[1].type);
+ EXPECT_EQ("3rd_type", *test_node->exact_matches()[2].type);
// Check the long string node
auto expect_empty_one_child = [](auto* node) {
ASSERT_NE(nullptr, node);
EXPECT_EQ(nullptr, node->context());
- EXPECT_EQ(nullptr, node->schema());
+ EXPECT_EQ(nullptr, node->type());
EXPECT_EQ(0U, node->prefixes().size());
EXPECT_EQ(0U, node->exact_matches().size());
EXPECT_EQ(1U, node->children().size());
@@ -120,8 +120,8 @@
EXPECT_EQ("string", property_entry.name);
ASSERT_NE(nullptr, property_entry.context);
EXPECT_EQ("4th", *property_entry.context);
- ASSERT_NE(nullptr, property_entry.schema);
- EXPECT_EQ("4th_schema", *property_entry.schema);
+ ASSERT_NE(nullptr, property_entry.type);
+ EXPECT_EQ("4th_type", *property_entry.type);
}
}
diff --git a/property_service/libpropertyinfoserializer/trie_serializer.cpp b/property_service/libpropertyinfoserializer/trie_serializer.cpp
index 5326537..adeed1b 100644
--- a/property_service/libpropertyinfoserializer/trie_serializer.cpp
+++ b/property_service/libpropertyinfoserializer/trie_serializer.cpp
@@ -43,15 +43,15 @@
uint32_t context_index = property_entry.context != nullptr && !property_entry.context->empty()
? serialized_info()->FindContextIndex(property_entry.context->c_str())
: ~0u;
- uint32_t schema_index = property_entry.schema != nullptr && !property_entry.schema->empty()
- ? serialized_info()->FindSchemaIndex(property_entry.schema->c_str())
- : ~0u;
+ uint32_t type_index = property_entry.type != nullptr && !property_entry.type->empty()
+ ? serialized_info()->FindTypeIndex(property_entry.type->c_str())
+ : ~0u;
uint32_t offset;
auto serialized_property_entry = arena_->AllocateObject<PropertyEntry>(&offset);
serialized_property_entry->name_offset = arena_->AllocateAndWriteString(property_entry.name);
serialized_property_entry->namelen = property_entry.name.size();
serialized_property_entry->context_index = context_index;
- serialized_property_entry->schema_index = schema_index;
+ serialized_property_entry->type_index = type_index;
return offset;
}
@@ -122,9 +122,9 @@
header->contexts_offset = arena_->size();
SerializeStrings(trie_builder.contexts());
- // Store where we're about to write the schemas.
- header->schemas_offset = arena_->size();
- SerializeStrings(trie_builder.schemas());
+ // Store where we're about to write the types.
+ header->types_offset = arena_->size();
+ SerializeStrings(trie_builder.types());
// We need to store size() up to this point now for Find*Offset() to work.
header->size = arena_->size();
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index 19269d8..ca992d6 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -125,24 +125,81 @@
bcp_md5 :=
bcp_dep :=
-# If PLATFORM_VNDK_VERSION is defined and not "current", generate versioned
-# module names for ld.config.txt, llndk.libraries.txt and vndksp.libraries.txt
-# files.
-define versioned_module_name
+# If BOARD_VNDK_VERSION is defined, append PLATFORM_VNDK_VERSION to base name.
+define append_vndk_version
$(strip \
- $(if $(filter-out current,$(PLATFORM_VNDK_VERSION)), \
- $(basename $(LOCAL_MODULE)).$(PLATFORM_VNDK_VERSION)$(suffix $(LOCAL_MODULE)), \
- $(LOCAL_MODULE) \
+ $(if $(BOARD_VNDK_VERSION), \
+ $(basename $(1)).$(PLATFORM_VNDK_VERSION)$(suffix $(1)), \
+ $(1) \
) \
)
endef
+# Update namespace configuration file with library lists and VNDK version
+#
+# $(1): Input source file (ld.config.txt)
+# $(2): Output built module
+# $(3): VNDK version suffix
+define update_and_install_ld_config
+llndk_libraries := $(call normalize-path-list,$(addsuffix .so,\
+ $(filter-out $(VNDK_PRIVATE_LIBRARIES),$(LLNDK_LIBRARIES))))
+private_llndk_libraries := $(call normalize-path-list,$(addsuffix .so,\
+ $(filter $(VNDK_PRIVATE_LIBRARIES),$(LLNDK_LIBRARIES))))
+vndk_sameprocess_libraries := $(call normalize-path-list,$(addsuffix .so,\
+ $(filter-out $(VNDK_PRIVATE_LIBRARIES),$(VNDK_SAMEPROCESS_LIBRARIES))))
+vndk_core_libraries := $(call normalize-path-list,$(addsuffix .so,\
+ $(filter-out $(VNDK_PRIVATE_LIBRARIES),$(VNDK_CORE_LIBRARIES))))
+sanitizer_runtime_libraries := $(call normalize-path-list,$(addsuffix .so,\
+ $(ADDRESS_SANITIZER_RUNTIME_LIBRARY) \
+ $(UBSAN_RUNTIME_LIBRARY) \
+ $(TSAN_RUNTIME_LIBRARY) \
+ $(2ND_ADDRESS_SANITIZER_RUNTIME_LIBRARY) \
+ $(2ND_UBSAN_RUNTIME_LIBRARY) \
+ $(2ND_TSAN_RUNTIME_LIBRARY)))
+# If BOARD_VNDK_VERSION is not defined, VNDK version suffix will not be used.
+vndk_version_suffix := $(if $(strip $(3)),-$(strip $(3)))
+
+$(2): PRIVATE_LLNDK_LIBRARIES := $$(llndk_libraries)
+$(2): PRIVATE_PRIVATE_LLNDK_LIBRARIES := $$(private_llndk_libraries)
+$(2): PRIVATE_VNDK_SAMEPROCESS_LIBRARIES := $$(vndk_sameprocess_libraries)
+$(2): PRIVATE_VNDK_CORE_LIBRARIES := $$(vndk_core_libraries)
+$(2): PRIVATE_SANITIZER_RUNTIME_LIBRARIES := $$(sanitizer_runtime_libraries)
+$(2): PRIVATE_VNDK_VERSION := $$(vndk_version_suffix)
+$(2): $(1)
+ @echo "Generate: $$< -> $$@"
+ @mkdir -p $$(dir $$@)
+ $$(hide) sed -e 's?%LLNDK_LIBRARIES%?$$(PRIVATE_LLNDK_LIBRARIES)?g' $$< >$$@
+ $$(hide) sed -i -e 's?%PRIVATE_LLNDK_LIBRARIES%?$$(PRIVATE_PRIVATE_LLNDK_LIBRARIES)?g' $$@
+ $$(hide) sed -i -e 's?%VNDK_SAMEPROCESS_LIBRARIES%?$$(PRIVATE_VNDK_SAMEPROCESS_LIBRARIES)?g' $$@
+ $$(hide) sed -i -e 's?%VNDK_CORE_LIBRARIES%?$$(PRIVATE_VNDK_CORE_LIBRARIES)?g' $$@
+ $$(hide) sed -i -e 's?%SANITIZER_RUNTIME_LIBRARIES%?$$(PRIVATE_SANITIZER_RUNTIME_LIBRARIES)?g' $$@
+ $$(hide) sed -i -e 's?%VNDK_VER%?$$(PRIVATE_VNDK_VERSION)?g' $$@
+
+llndk_libraries :=
+private_llndk_libraries :=
+vndk_sameprocess_libraries :=
+vndk_core_libraries :=
+sanitizer_runtime_libraries :=
+vndk_version_suffix :=
+endef # update_and_install_ld_config
+
#######################################
# ld.config.txt
+#
+# For VNDK enforced devices that have defined BOARD_VNDK_VERSION, use
+# "ld.config.txt.in" as a source file. This configuration includes strict VNDK
+# run-time restrictions for vendor process.
+# Other treblized devices, that have not defined BOARD_VNDK_VERSION or that
+# have set BOARD_VNDK_RUNTIME_DISABLE to true, use "ld.config.txt" as a source
+# file. This configuration does not have strict VNDK run-time restrictions.
+# If the device is not treblized, use "ld.config.legacy.txt" for legacy
+# namespace configuration.
include $(CLEAR_VARS)
+LOCAL_MODULE := ld.config.txt
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
_enforce_vndk_at_runtime := false
-
ifdef BOARD_VNDK_VERSION
ifneq ($(BOARD_VNDK_RUNTIME_DISABLE),true)
_enforce_vndk_at_runtime := true
@@ -150,65 +207,52 @@
endif
ifeq ($(_enforce_vndk_at_runtime),true)
-LOCAL_MODULE := ld.config.txt
-LOCAL_MODULE_CLASS := ETC
-LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
-LOCAL_MODULE_STEM := $(call versioned_module_name)
+# for VNDK enforced devices
+LOCAL_MODULE_STEM := $(call append_vndk_version,$(LOCAL_MODULE))
include $(BUILD_SYSTEM)/base_rules.mk
+$(eval $(call update_and_install_ld_config,\
+ $(LOCAL_PATH)/etc/ld.config.txt.in,\
+ $(LOCAL_BUILT_MODULE),\
+ $(PLATFORM_VNDK_VERSION)))
-llndk_libraries := $(call normalize-path-list,$(addsuffix .so,\
-$(filter-out $(VNDK_PRIVATE_LIBRARIES),$(LLNDK_LIBRARIES))))
+else ifeq ($(PRODUCT_TREBLE_LINKER_NAMESPACES)|$(SANITIZE_TARGET),true|)
+# for treblized but VNDK non-enforced devices
+LOCAL_MODULE_STEM := $(call append_vndk_version,$(LOCAL_MODULE))
+include $(BUILD_SYSTEM)/base_rules.mk
+$(eval $(call update_and_install_ld_config,\
+ $(LOCAL_PATH)/etc/ld.config.txt,\
+ $(LOCAL_BUILT_MODULE),\
+ $(if $(BOARD_VNDK_VERSION),$(PLATFORM_VNDK_VERSION))))
-private_llndk_libraries := $(call normalize-path-list,$(addsuffix .so,\
-$(filter $(VNDK_PRIVATE_LIBRARIES),$(LLNDK_LIBRARIES))))
-
-vndk_sameprocess_libraries := $(call normalize-path-list,$(addsuffix .so,\
-$(filter-out $(VNDK_PRIVATE_LIBRARIES),$(VNDK_SAMEPROCESS_LIBRARIES))))
-
-vndk_core_libraries := $(call normalize-path-list,$(addsuffix .so,\
-$(filter-out $(VNDK_PRIVATE_LIBRARIES),$(VNDK_CORE_LIBRARIES))))
-
-sanitizer_runtime_libraries := $(call normalize-path-list,$(addsuffix .so,\
-$(ADDRESS_SANITIZER_RUNTIME_LIBRARY) \
-$(UBSAN_RUNTIME_LIBRARY) \
-$(TSAN_RUNTIME_LIBRARY) \
-$(2ND_ADDRESS_SANITIZER_RUNTIME_LIBRARY) \
-$(2ND_UBSAN_RUNTIME_LIBRARY) \
-$(2ND_TSAN_RUNTIME_LIBRARY)))
-
-$(LOCAL_BUILT_MODULE): PRIVATE_LLNDK_LIBRARIES := $(llndk_libraries)
-$(LOCAL_BUILT_MODULE): PRIVATE_PRIVATE_LLNDK_LIBRARIES := $(private_llndk_libraries)
-$(LOCAL_BUILT_MODULE): PRIVATE_VNDK_SAMEPROCESS_LIBRARIES := $(vndk_sameprocess_libraries)
-$(LOCAL_BUILT_MODULE): PRIVATE_LLNDK_PRIVATE_LIBRARIES := $(llndk_private_libraries)
-$(LOCAL_BUILT_MODULE): PRIVATE_VNDK_CORE_LIBRARIES := $(vndk_core_libraries)
-$(LOCAL_BUILT_MODULE): PRIVATE_SANITIZER_RUNTIME_LIBRARIES := $(sanitizer_runtime_libraries)
-$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/etc/ld.config.txt.in
- @echo "Generate: $< -> $@"
- @mkdir -p $(dir $@)
- $(hide) sed -e 's?%LLNDK_LIBRARIES%?$(PRIVATE_LLNDK_LIBRARIES)?g' $< >$@
- $(hide) sed -i -e 's?%PRIVATE_LLNDK_LIBRARIES%?$(PRIVATE_PRIVATE_LLNDK_LIBRARIES)?g' $@
- $(hide) sed -i -e 's?%VNDK_SAMEPROCESS_LIBRARIES%?$(PRIVATE_VNDK_SAMEPROCESS_LIBRARIES)?g' $@
- $(hide) sed -i -e 's?%VNDK_CORE_LIBRARIES%?$(PRIVATE_VNDK_CORE_LIBRARIES)?g' $@
- $(hide) sed -i -e 's?%SANITIZER_RUNTIME_LIBRARIES%?$(PRIVATE_SANITIZER_RUNTIME_LIBRARIES)?g' $@
-
-llndk_libraries :=
-vndk_sameprocess_libraries :=
-vndk_core_libraries :=
-sanitizer_runtime_libraries :=
-else # if _enforce_vndk_at_runtime is not true
-
-LOCAL_MODULE := ld.config.txt
-ifeq ($(PRODUCT_TREBLE_LINKER_NAMESPACES)|$(SANITIZE_TARGET),true|)
- LOCAL_SRC_FILES := etc/ld.config.txt
- LOCAL_MODULE_STEM := $(call versioned_module_name)
else
- LOCAL_SRC_FILES := etc/ld.config.legacy.txt
- LOCAL_MODULE_STEM := $(LOCAL_MODULE)
-endif
+# for legacy non-treblized devices
+LOCAL_SRC_FILES := etc/ld.config.legacy.txt
+LOCAL_MODULE_STEM := $(LOCAL_MODULE)
+include $(BUILD_PREBUILT)
+
+endif # if _enforce_vndk_at_runtime is true
+
+_enforce_vndk_at_runtime :=
+
+#######################################
+# ld.config.noenforce.txt
+#
+# This file is a temporary configuration file only for GSI. Originally GSI has
+# BOARD_VNDK_VERSION defined and has strict VNDK enforcing rule based on
+# "ld.config.txt.in". However for the devices, that have not defined
+# BOARD_VNDK_VERSION, GSI provides this configuration file which is based on
+# "ld.config.txt".
+# Do not install this file for the devices other than GSI.
+include $(CLEAR_VARS)
+LOCAL_MODULE := ld.config.noenforce.txt
LOCAL_MODULE_CLASS := ETC
LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
-include $(BUILD_PREBUILT)
-endif
+LOCAL_MODULE_STEM := $(LOCAL_MODULE)
+include $(BUILD_SYSTEM)/base_rules.mk
+$(eval $(call update_and_install_ld_config,\
+ $(LOCAL_PATH)/etc/ld.config.txt,\
+ $(LOCAL_BUILT_MODULE),\
+ $(PLATFORM_VNDK_VERSION)))
#######################################
# llndk.libraries.txt
@@ -216,7 +260,7 @@
LOCAL_MODULE := llndk.libraries.txt
LOCAL_MODULE_CLASS := ETC
LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
-LOCAL_MODULE_STEM := $(call versioned_module_name)
+LOCAL_MODULE_STEM := $(call append_vndk_version,$(LOCAL_MODULE))
include $(BUILD_SYSTEM)/base_rules.mk
$(LOCAL_BUILT_MODULE): PRIVATE_LLNDK_LIBRARIES := $(LLNDK_LIBRARIES)
$(LOCAL_BUILT_MODULE):
@@ -232,7 +276,7 @@
LOCAL_MODULE := vndksp.libraries.txt
LOCAL_MODULE_CLASS := ETC
LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
-LOCAL_MODULE_STEM := $(call versioned_module_name)
+LOCAL_MODULE_STEM := $(call append_vndk_version,$(LOCAL_MODULE))
include $(BUILD_SYSTEM)/base_rules.mk
$(LOCAL_BUILT_MODULE): PRIVATE_VNDK_SAMEPROCESS_LIBRARIES := $(VNDK_SAMEPROCESS_LIBRARIES)
$(LOCAL_BUILT_MODULE):
diff --git a/rootdir/OWNERS b/rootdir/OWNERS
index f335715..6029ae7 100644
--- a/rootdir/OWNERS
+++ b/rootdir/OWNERS
@@ -1,3 +1,4 @@
+jeffv@google.com
jiyong@google.com
smoreland@google.com
tomcherry@google.com
diff --git a/rootdir/etc/ld.config.txt b/rootdir/etc/ld.config.txt
index b86104d..5d97a73 100644
--- a/rootdir/etc/ld.config.txt
+++ b/rootdir/etc/ld.config.txt
@@ -128,7 +128,7 @@
namespace.rs.search.paths = /odm/${LIB}/vndk-sp
namespace.rs.search.paths += /vendor/${LIB}/vndk-sp
-namespace.rs.search.paths += /system/${LIB}/vndk-sp${VNDK_VER}
+namespace.rs.search.paths += /system/${LIB}/vndk-sp%VNDK_VER%
namespace.rs.search.paths += /odm/${LIB}
namespace.rs.search.paths += /vendor/${LIB}
@@ -140,8 +140,8 @@
namespace.rs.asan.search.paths += /odm/${LIB}/vndk-sp
namespace.rs.asan.search.paths += /data/asan/vendor/${LIB}/vndk-sp
namespace.rs.asan.search.paths += /vendor/${LIB}/vndk-sp
-namespace.rs.asan.search.paths += /data/asan/system/${LIB}/vndk-sp${VNDK_VER}
-namespace.rs.asan.search.paths += /system/${LIB}/vndk-sp${VNDK_VER}
+namespace.rs.asan.search.paths += /data/asan/system/${LIB}/vndk-sp%VNDK_VER%
+namespace.rs.asan.search.paths += /system/${LIB}/vndk-sp%VNDK_VER%
namespace.rs.asan.search.paths += /data/asan/odm/${LIB}
namespace.rs.asan.search.paths += /odm/${LIB}
namespace.rs.asan.search.paths += /data/asan/vendor/${LIB}
@@ -198,7 +198,7 @@
namespace.vndk.search.paths = /odm/${LIB}/vndk-sp
namespace.vndk.search.paths += /vendor/${LIB}/vndk-sp
-namespace.vndk.search.paths += /system/${LIB}/vndk-sp${VNDK_VER}
+namespace.vndk.search.paths += /system/${LIB}/vndk-sp%VNDK_VER%
namespace.vndk.permitted.paths = /odm/${LIB}/hw
namespace.vndk.permitted.paths += /odm/${LIB}/egl
@@ -209,8 +209,8 @@
namespace.vndk.asan.search.paths += /odm/${LIB}/vndk-sp
namespace.vndk.asan.search.paths += /data/asan/vendor/${LIB}/vndk-sp
namespace.vndk.asan.search.paths += /vendor/${LIB}/vndk-sp
-namespace.vndk.asan.search.paths += /data/asan/system/${LIB}/vndk-sp${VNDK_VER}
-namespace.vndk.asan.search.paths += /system/${LIB}/vndk-sp${VNDK_VER}
+namespace.vndk.asan.search.paths += /data/asan/system/${LIB}/vndk-sp%VNDK_VER%
+namespace.vndk.asan.search.paths += /system/${LIB}/vndk-sp%VNDK_VER%
namespace.vndk.asan.permitted.paths = /data/asan/odm/${LIB}/hw
namespace.vndk.asan.permitted.paths += /odm/${LIB}/hw
@@ -254,8 +254,8 @@
namespace.default.search.paths += /vendor/${LIB}/vndk-sp
# Access to system libraries are allowed
-namespace.default.search.paths += /system/${LIB}/vndk${VNDK_VER}
-namespace.default.search.paths += /system/${LIB}/vndk-sp${VNDK_VER}
+namespace.default.search.paths += /system/${LIB}/vndk%VNDK_VER%
+namespace.default.search.paths += /system/${LIB}/vndk-sp%VNDK_VER%
namespace.default.search.paths += /system/${LIB}
namespace.default.asan.search.paths = /data/asan/odm/${LIB}
@@ -270,9 +270,9 @@
namespace.default.asan.search.paths += /vendor/${LIB}/vndk
namespace.default.asan.search.paths += /data/asan/vendor/${LIB}/vndk-sp
namespace.default.asan.search.paths += /vendor/${LIB}/vndk-sp
-namespace.default.asan.search.paths += /data/asan/system/${LIB}/vndk${VNDK_VER}
-namespace.default.asan.search.paths += /system/${LIB}/vndk${VNDK_VER}
-namespace.default.asan.search.paths += /data/asan/system/${LIB}/vndk-sp${VNDK_VER}
-namespace.default.asan.search.paths += /system/${LIB}/vndk-sp${VNDK_VER}
+namespace.default.asan.search.paths += /data/asan/system/${LIB}/vndk%VNDK_VER%
+namespace.default.asan.search.paths += /system/${LIB}/vndk%VNDK_VER%
+namespace.default.asan.search.paths += /data/asan/system/${LIB}/vndk-sp%VNDK_VER%
+namespace.default.asan.search.paths += /system/${LIB}/vndk-sp%VNDK_VER%
namespace.default.asan.search.paths += /data/asan/system/${LIB}
namespace.default.asan.search.paths += /system/${LIB}
diff --git a/rootdir/etc/ld.config.txt.in b/rootdir/etc/ld.config.txt.in
index ffc4359..cad09c3 100644
--- a/rootdir/etc/ld.config.txt.in
+++ b/rootdir/etc/ld.config.txt.in
@@ -131,7 +131,7 @@
namespace.rs.search.paths = /odm/${LIB}/vndk-sp
namespace.rs.search.paths += /vendor/${LIB}/vndk-sp
-namespace.rs.search.paths += /system/${LIB}/vndk-sp${VNDK_VER}
+namespace.rs.search.paths += /system/${LIB}/vndk-sp%VNDK_VER%
namespace.rs.search.paths += /odm/${LIB}
namespace.rs.search.paths += /vendor/${LIB}
@@ -143,8 +143,8 @@
namespace.rs.asan.search.paths += /odm/${LIB}/vndk-sp
namespace.rs.asan.search.paths += /data/asan/vendor/${LIB}/vndk-sp
namespace.rs.asan.search.paths += /vendor/${LIB}/vndk-sp
-namespace.rs.asan.search.paths += /data/asan/system/${LIB}/vndk-sp${VNDK_VER}
-namespace.rs.asan.search.paths += /system/${LIB}/vndk-sp${VNDK_VER}
+namespace.rs.asan.search.paths += /data/asan/system/${LIB}/vndk-sp%VNDK_VER%
+namespace.rs.asan.search.paths += /system/${LIB}/vndk-sp%VNDK_VER%
namespace.rs.asan.search.paths += /data/asan/odm/${LIB}
namespace.rs.asan.search.paths += /odm/${LIB}
namespace.rs.asan.search.paths += /data/asan/vendor/${LIB}
@@ -176,21 +176,21 @@
namespace.vndk.search.paths = /odm/${LIB}/vndk-sp
namespace.vndk.search.paths += /vendor/${LIB}/vndk-sp
-namespace.vndk.search.paths += /system/${LIB}/vndk-sp${VNDK_VER}
+namespace.vndk.search.paths += /system/${LIB}/vndk-sp%VNDK_VER%
namespace.vndk.permitted.paths = /odm/${LIB}/hw
namespace.vndk.permitted.paths += /odm/${LIB}/egl
namespace.vndk.permitted.paths += /vendor/${LIB}/hw
namespace.vndk.permitted.paths += /vendor/${LIB}/egl
# This is exceptionally required since android.hidl.memory@1.0-impl.so is here
-namespace.vndk.permitted.paths += /system/${LIB}/vndk-sp${VNDK_VER}/hw
+namespace.vndk.permitted.paths += /system/${LIB}/vndk-sp%VNDK_VER%/hw
namespace.vndk.asan.search.paths = /data/asan/odm/${LIB}/vndk-sp
namespace.vndk.asan.search.paths += /odm/${LIB}/vndk-sp
namespace.vndk.asan.search.paths += /data/asan/vendor/${LIB}/vndk-sp
namespace.vndk.asan.search.paths += /vendor/${LIB}/vndk-sp
-namespace.vndk.asan.search.paths += /data/asan/system/${LIB}/vndk-sp${VNDK_VER}
-namespace.vndk.asan.search.paths += /system/${LIB}/vndk-sp${VNDK_VER}
+namespace.vndk.asan.search.paths += /data/asan/system/${LIB}/vndk-sp%VNDK_VER%
+namespace.vndk.asan.search.paths += /system/${LIB}/vndk-sp%VNDK_VER%
namespace.vndk.asan.permitted.paths = /data/asan/odm/${LIB}/hw
namespace.vndk.asan.permitted.paths += /odm/${LIB}/hw
@@ -201,8 +201,8 @@
namespace.vndk.asan.permitted.paths += /data/asan/vendor/${LIB}/egl
namespace.vndk.asan.permitted.paths += /vendor/${LIB}/egl
-namespace.vndk.asan.permitted.paths += /data/asan/system/${LIB}/vndk-sp${VNDK_VER}/hw
-namespace.vndk.asan.permitted.paths += /system/${LIB}/vndk-sp${VNDK_VER}/hw
+namespace.vndk.asan.permitted.paths += /data/asan/system/${LIB}/vndk-sp%VNDK_VER%/hw
+namespace.vndk.asan.permitted.paths += /system/${LIB}/vndk-sp%VNDK_VER%/hw
# When these NDK libs are required inside this namespace, then it is redirected
# to the default namespace. This is possible since their ABI is stable across
@@ -274,13 +274,13 @@
###############################################################################
namespace.vndk.isolated = false
-namespace.vndk.search.paths = /system/${LIB}/vndk-sp${VNDK_VER}
-namespace.vndk.search.paths += /system/${LIB}/vndk${VNDK_VER}
+namespace.vndk.search.paths = /system/${LIB}/vndk-sp%VNDK_VER%
+namespace.vndk.search.paths += /system/${LIB}/vndk%VNDK_VER%
-namespace.vndk.asan.search.paths = /data/asan/system/${LIB}/vndk-sp${VNDK_VER}
-namespace.vndk.asan.search.paths += /system/${LIB}/vndk-sp${VNDK_VER}
-namespace.vndk.asan.search.paths += /data/asan/system/${LIB}/vndk${VNDK_VER}
-namespace.vndk.asan.search.paths += /system/${LIB}/vndk${VNDK_VER}
+namespace.vndk.asan.search.paths = /data/asan/system/${LIB}/vndk-sp%VNDK_VER%
+namespace.vndk.asan.search.paths += /system/${LIB}/vndk-sp%VNDK_VER%
+namespace.vndk.asan.search.paths += /data/asan/system/${LIB}/vndk%VNDK_VER%
+namespace.vndk.asan.search.paths += /system/${LIB}/vndk%VNDK_VER%
# When these NDK libs are required inside this namespace, then it is redirected
# to the system namespace. This is possible since their ABI is stable across
diff --git a/rootdir/init.rc b/rootdir/init.rc
index b459594..e3c791e 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -218,6 +218,12 @@
# This is needed by any process that uses socket tagging.
chmod 0644 /dev/xt_qtaguid
+ mkdir /dev/cg2_bpf
+ mount cgroup2 cg2_bpf /dev/cg2_bpf
+ chown root root /dev/cg2_bpf
+ chmod 0600 /dev/cg2_bpf
+ mount bpf bpf /sys/fs/bpf
+
# Create location for fs_mgr to store abbreviated output from filesystem
# checker programs.
mkdir /dev/fscklogs 0770 root system
@@ -462,6 +468,8 @@
mkdir /data/app 0771 system system
mkdir /data/property 0700 root root
mkdir /data/tombstones 0771 system system
+ mkdir /data/vendor/tombstones 0771 root root
+ mkdir /data/vendor/tombstones/wifi 0771 wifi wifi
# create dalvik-cache, so as to enforce our permissions
mkdir /data/dalvik-cache 0771 root root
diff --git a/sdcard/Android.bp b/sdcard/Android.bp
new file mode 100644
index 0000000..c096587
--- /dev/null
+++ b/sdcard/Android.bp
@@ -0,0 +1,17 @@
+cc_binary {
+ srcs: ["sdcard.cpp"],
+ name: "sdcard",
+ cflags: [
+ "-Wall",
+ "-Wno-unused-parameter",
+ "-Werror",
+ ],
+ shared_libs: [
+ "libbase",
+ "libcutils",
+ "libminijail",
+ ],
+ sanitize: {
+ misc_undefined: ["integer"],
+ },
+}
diff --git a/sdcard/Android.mk b/sdcard/Android.mk
deleted file mode 100644
index 5b4dc58..0000000
--- a/sdcard/Android.mk
+++ /dev/null
@@ -1,12 +0,0 @@
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := sdcard.cpp fuse.cpp
-LOCAL_MODULE := sdcard
-LOCAL_CFLAGS := -Wall -Wno-unused-parameter -Werror
-LOCAL_SHARED_LIBRARIES := libbase libcutils libminijail libpackagelistparser
-
-LOCAL_SANITIZE := integer
-
-include $(BUILD_EXECUTABLE)
diff --git a/sdcard/fuse.cpp b/sdcard/fuse.cpp
deleted file mode 100644
index 10d0f04..0000000
--- a/sdcard/fuse.cpp
+++ /dev/null
@@ -1,1476 +0,0 @@
-/*
- * Copyright (C) 2010 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 <string.h>
-#include <unistd.h>
-
-#define LOG_TAG "sdcard"
-
-#include "fuse.h"
-
-#include <android-base/logging.h>
-
-/* FUSE_CANONICAL_PATH is not currently upstreamed */
-#define FUSE_CANONICAL_PATH 2016
-
-#define FUSE_UNKNOWN_INO 0xffffffff
-
-/* Pseudo-error constant used to indicate that no fuse status is needed
- * or that a reply has already been written. */
-#define NO_STATUS 1
-
-static inline void *id_to_ptr(__u64 nid)
-{
- return (void *) (uintptr_t) nid;
-}
-
-static inline __u64 ptr_to_id(void *ptr)
-{
- return (__u64) (uintptr_t) ptr;
-}
-
-static void acquire_node_locked(struct node* node)
-{
- node->refcount++;
- DLOG(INFO) << "ACQUIRE " << std::hex << node << std::dec
- << " (" << node->name << ") rc=" << node->refcount;
-}
-
-static void remove_node_from_parent_locked(struct node* node);
-
-static void release_node_locked(struct node* node)
-{
- DLOG(INFO) << "RELEASE " << std::hex << node << std::dec
- << " (" << node->name << ") rc=" << node->refcount;
- if (node->refcount > 0) {
- node->refcount--;
- if (!node->refcount) {
- DLOG(INFO) << "DESTROY " << std::hex << node << std::dec << " (" << node->name << ")";
- remove_node_from_parent_locked(node);
-
- /* TODO: remove debugging - poison memory */
- memset(node->name, 0xef, node->namelen);
- free(node->name);
- free(node->actual_name);
- memset(node, 0xfc, sizeof(*node));
- free(node);
- }
- } else {
- LOG(ERROR) << std::hex << node << std::dec << " refcount=0";
- }
-}
-
-static void add_node_to_parent_locked(struct node *node, struct node *parent) {
- node->parent = parent;
- node->next = parent->child;
- parent->child = node;
- acquire_node_locked(parent);
-}
-
-static void remove_node_from_parent_locked(struct node* node)
-{
- if (node->parent) {
- if (node->parent->child == node) {
- node->parent->child = node->parent->child->next;
- } else {
- struct node *node2;
- node2 = node->parent->child;
- while (node2->next != node)
- node2 = node2->next;
- node2->next = node->next;
- }
- release_node_locked(node->parent);
- node->parent = NULL;
- node->next = NULL;
- }
-}
-
-/* Gets the absolute path to a node into the provided buffer.
- *
- * Populates 'buf' with the path and returns the length of the path on success,
- * or returns -1 if the path is too long for the provided buffer.
- */
-static ssize_t get_node_path_locked(struct node* node, char* buf, size_t bufsize) {
- const char* name;
- size_t namelen;
- if (node->graft_path) {
- name = node->graft_path;
- namelen = node->graft_pathlen;
- } else if (node->actual_name) {
- name = node->actual_name;
- namelen = node->namelen;
- } else {
- name = node->name;
- namelen = node->namelen;
- }
-
- if (bufsize < namelen + 1) {
- return -1;
- }
-
- ssize_t pathlen = 0;
- if (node->parent && node->graft_path == NULL) {
- pathlen = get_node_path_locked(node->parent, buf, bufsize - namelen - 1);
- if (pathlen < 0) {
- return -1;
- }
- buf[pathlen++] = '/';
- }
-
- memcpy(buf + pathlen, name, namelen + 1); /* include trailing \0 */
- return pathlen + namelen;
-}
-
-/* Finds the absolute path of a file within a given directory.
- * Performs a case-insensitive search for the file and sets the buffer to the path
- * of the first matching file. If 'search' is zero or if no match is found, sets
- * the buffer to the path that the file would have, assuming the name were case-sensitive.
- *
- * Populates 'buf' with the path and returns the actual name (within 'buf') on success,
- * or returns NULL if the path is too long for the provided buffer.
- */
-static char* find_file_within(const char* path, const char* name,
- char* buf, size_t bufsize, int search)
-{
- size_t pathlen = strlen(path);
- size_t namelen = strlen(name);
- size_t childlen = pathlen + namelen + 1;
- char* actual;
-
- if (bufsize <= childlen) {
- return NULL;
- }
-
- memcpy(buf, path, pathlen);
- buf[pathlen] = '/';
- actual = buf + pathlen + 1;
- memcpy(actual, name, namelen + 1);
-
- if (search && access(buf, F_OK)) {
- struct dirent* entry;
- DIR* dir = opendir(path);
- if (!dir) {
- PLOG(ERROR) << "opendir(" << path << ") failed";
- return actual;
- }
- while ((entry = readdir(dir))) {
- if (!strcasecmp(entry->d_name, name)) {
- /* we have a match - replace the name, don't need to copy the null again */
- memcpy(actual, entry->d_name, namelen);
- break;
- }
- }
- closedir(dir);
- }
- return actual;
-}
-
-static void attr_from_stat(struct fuse* fuse, struct fuse_attr *attr,
- const struct stat *s, const struct node* node) {
- attr->ino = node->ino;
- attr->size = s->st_size;
- attr->blocks = s->st_blocks;
- attr->atime = s->st_atim.tv_sec;
- attr->mtime = s->st_mtim.tv_sec;
- attr->ctime = s->st_ctim.tv_sec;
- attr->atimensec = s->st_atim.tv_nsec;
- attr->mtimensec = s->st_mtim.tv_nsec;
- attr->ctimensec = s->st_ctim.tv_nsec;
- attr->mode = s->st_mode;
- attr->nlink = s->st_nlink;
-
- attr->uid = node->uid;
-
- if (fuse->gid == AID_SDCARD_RW) {
- /* As an optimization, certain trusted system components only run
- * as owner but operate across all users. Since we're now handing
- * out the sdcard_rw GID only to trusted apps, we're okay relaxing
- * the user boundary enforcement for the default view. The UIDs
- * assigned to app directories are still multiuser aware. */
- attr->gid = AID_SDCARD_RW;
- } else {
- attr->gid = multiuser_get_uid(node->userid, fuse->gid);
- }
-
- int visible_mode = 0775 & ~fuse->mask;
- if (node->perm == PERM_PRE_ROOT) {
- /* Top of multi-user view should always be visible to ensure
- * secondary users can traverse inside. */
- visible_mode = 0711;
- } else if (node->under_android) {
- /* Block "other" access to Android directories, since only apps
- * belonging to a specific user should be in there; we still
- * leave +x open for the default view. */
- if (fuse->gid == AID_SDCARD_RW) {
- visible_mode = visible_mode & ~0006;
- } else {
- visible_mode = visible_mode & ~0007;
- }
- }
- int owner_mode = s->st_mode & 0700;
- int filtered_mode = visible_mode & (owner_mode | (owner_mode >> 3) | (owner_mode >> 6));
- attr->mode = (attr->mode & S_IFMT) | filtered_mode;
-}
-
-static int touch(char* path, mode_t mode) {
- int fd = TEMP_FAILURE_RETRY(open(path, O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW | O_CLOEXEC,
- mode));
- if (fd == -1) {
- if (errno == EEXIST) {
- return 0;
- } else {
- PLOG(ERROR) << "open(" << path << ") failed";
- return -1;
- }
- }
- close(fd);
- return 0;
-}
-
-static void derive_permissions_locked(struct fuse* fuse, struct node *parent,
- struct node *node) {
- appid_t appid;
-
- /* By default, each node inherits from its parent */
- node->perm = PERM_INHERIT;
- node->userid = parent->userid;
- node->uid = parent->uid;
- node->under_android = parent->under_android;
-
- /* Derive custom permissions based on parent and current node */
- switch (parent->perm) {
- case PERM_INHERIT:
- /* Already inherited above */
- break;
- case PERM_PRE_ROOT:
- /* Legacy internal layout places users at top level */
- node->perm = PERM_ROOT;
- node->userid = strtoul(node->name, NULL, 10);
- break;
- case PERM_ROOT:
- /* Assume masked off by default. */
- if (!strcasecmp(node->name, "Android")) {
- /* App-specific directories inside; let anyone traverse */
- node->perm = PERM_ANDROID;
- node->under_android = true;
- }
- break;
- case PERM_ANDROID:
- if (!strcasecmp(node->name, "data")) {
- /* App-specific directories inside; let anyone traverse */
- node->perm = PERM_ANDROID_DATA;
- } else if (!strcasecmp(node->name, "obb")) {
- /* App-specific directories inside; let anyone traverse */
- node->perm = PERM_ANDROID_OBB;
- /* Single OBB directory is always shared */
- node->graft_path = fuse->global->obb_path;
- node->graft_pathlen = strlen(fuse->global->obb_path);
- } else if (!strcasecmp(node->name, "media")) {
- /* App-specific directories inside; let anyone traverse */
- node->perm = PERM_ANDROID_MEDIA;
- }
- break;
- case PERM_ANDROID_DATA:
- case PERM_ANDROID_OBB:
- case PERM_ANDROID_MEDIA:
- const auto& iter = fuse->global->package_to_appid->find(node->name);
- if (iter != fuse->global->package_to_appid->end()) {
- appid = iter->second;
- node->uid = multiuser_get_uid(parent->userid, appid);
- }
- break;
- }
-}
-
-void derive_permissions_recursive_locked(struct fuse* fuse, struct node *parent) {
- struct node *node;
- for (node = parent->child; node; node = node->next) {
- derive_permissions_locked(fuse, parent, node);
- if (node->child) {
- derive_permissions_recursive_locked(fuse, node);
- }
- }
-}
-
-/* Kernel has already enforced everything we returned through
- * derive_permissions_locked(), so this is used to lock down access
- * even further, such as enforcing that apps hold sdcard_rw. */
-static bool check_caller_access_to_name(struct fuse* fuse,
- const struct fuse_in_header *hdr, const struct node* parent_node,
- const char* name, int mode) {
- /* Always block security-sensitive files at root */
- if (parent_node && parent_node->perm == PERM_ROOT) {
- if (!strcasecmp(name, "autorun.inf")
- || !strcasecmp(name, ".android_secure")
- || !strcasecmp(name, "android_secure")) {
- return false;
- }
- }
-
- /* Root always has access; access for any other UIDs should always
- * be controlled through packages.list. */
- if (hdr->uid == AID_ROOT) {
- return true;
- }
-
- /* No extra permissions to enforce */
- return true;
-}
-
-static bool check_caller_access_to_node(struct fuse* fuse,
- const struct fuse_in_header *hdr, const struct node* node, int mode) {
- return check_caller_access_to_name(fuse, hdr, node->parent, node->name, mode);
-}
-
-struct node *create_node_locked(struct fuse* fuse,
- struct node *parent, const char *name, const char* actual_name)
-{
- struct node *node;
- size_t namelen = strlen(name);
-
- // Detect overflows in the inode counter. "4 billion nodes should be enough
- // for everybody".
- if (fuse->global->inode_ctr == 0) {
- LOG(ERROR) << "No more inode numbers available";
- return NULL;
- }
-
- node = static_cast<struct node*>(calloc(1, sizeof(struct node)));
- if (!node) {
- return NULL;
- }
- node->name = static_cast<char*>(malloc(namelen + 1));
- if (!node->name) {
- free(node);
- return NULL;
- }
- memcpy(node->name, name, namelen + 1);
- if (strcmp(name, actual_name)) {
- node->actual_name = static_cast<char*>(malloc(namelen + 1));
- if (!node->actual_name) {
- free(node->name);
- free(node);
- return NULL;
- }
- memcpy(node->actual_name, actual_name, namelen + 1);
- }
- node->namelen = namelen;
- node->nid = ptr_to_id(node);
- node->ino = fuse->global->inode_ctr++;
- node->gen = fuse->global->next_generation++;
-
- node->deleted = false;
-
- derive_permissions_locked(fuse, parent, node);
- acquire_node_locked(node);
- add_node_to_parent_locked(node, parent);
- return node;
-}
-
-static int rename_node_locked(struct node *node, const char *name,
- const char* actual_name)
-{
- size_t namelen = strlen(name);
- int need_actual_name = strcmp(name, actual_name);
-
- /* make the storage bigger without actually changing the name
- * in case an error occurs part way */
- if (namelen > node->namelen) {
- char* new_name = static_cast<char*>(realloc(node->name, namelen + 1));
- if (!new_name) {
- return -ENOMEM;
- }
- node->name = new_name;
- if (need_actual_name && node->actual_name) {
- char* new_actual_name = static_cast<char*>(realloc(node->actual_name, namelen + 1));
- if (!new_actual_name) {
- return -ENOMEM;
- }
- node->actual_name = new_actual_name;
- }
- }
-
- /* update the name, taking care to allocate storage before overwriting the old name */
- if (need_actual_name) {
- if (!node->actual_name) {
- node->actual_name = static_cast<char*>(malloc(namelen + 1));
- if (!node->actual_name) {
- return -ENOMEM;
- }
- }
- memcpy(node->actual_name, actual_name, namelen + 1);
- } else {
- free(node->actual_name);
- node->actual_name = NULL;
- }
- memcpy(node->name, name, namelen + 1);
- node->namelen = namelen;
- return 0;
-}
-
-static struct node *lookup_node_by_id_locked(struct fuse *fuse, __u64 nid)
-{
- if (nid == FUSE_ROOT_ID) {
- return &fuse->global->root;
- } else {
- return static_cast<struct node*>(id_to_ptr(nid));
- }
-}
-
-static struct node* lookup_node_and_path_by_id_locked(struct fuse* fuse, __u64 nid,
- char* buf, size_t bufsize)
-{
- struct node* node = lookup_node_by_id_locked(fuse, nid);
- if (node && get_node_path_locked(node, buf, bufsize) < 0) {
- node = NULL;
- }
- return node;
-}
-
-static struct node *lookup_child_by_name_locked(struct node *node, const char *name)
-{
- for (node = node->child; node; node = node->next) {
- /* use exact string comparison, nodes that differ by case
- * must be considered distinct even if they refer to the same
- * underlying file as otherwise operations such as "mv x x"
- * will not work because the source and target nodes are the same. */
- if (!strcmp(name, node->name) && !node->deleted) {
- return node;
- }
- }
- return 0;
-}
-
-static struct node* acquire_or_create_child_locked(
- struct fuse* fuse, struct node* parent,
- const char* name, const char* actual_name)
-{
- struct node* child = lookup_child_by_name_locked(parent, name);
- if (child) {
- acquire_node_locked(child);
- } else {
- child = create_node_locked(fuse, parent, name, actual_name);
- }
- return child;
-}
-
-static void fuse_status(struct fuse *fuse, __u64 unique, int err)
-{
- struct fuse_out_header hdr;
- hdr.len = sizeof(hdr);
- hdr.error = err;
- hdr.unique = unique;
- ssize_t ret = TEMP_FAILURE_RETRY(write(fuse->fd, &hdr, sizeof(hdr)));
- if (ret == -1) {
- PLOG(ERROR) << "*** STATUS FAILED ***";
- } else if (static_cast<size_t>(ret) != sizeof(hdr)) {
- LOG(ERROR) << "*** STATUS FAILED: written " << ret << " expected "
- << sizeof(hdr) << " ***";
- }
-}
-
-static void fuse_reply(struct fuse *fuse, __u64 unique, void *data, int len)
-{
- struct fuse_out_header hdr;
- hdr.len = len + sizeof(hdr);
- hdr.error = 0;
- hdr.unique = unique;
-
- struct iovec vec[2];
- vec[0].iov_base = &hdr;
- vec[0].iov_len = sizeof(hdr);
- vec[1].iov_base = data;
- vec[1].iov_len = len;
-
- ssize_t ret = TEMP_FAILURE_RETRY(writev(fuse->fd, vec, 2));
- if (ret == -1) {
- PLOG(ERROR) << "*** REPLY FAILED ***";
- } else if (static_cast<size_t>(ret) != sizeof(hdr) + len) {
- LOG(ERROR) << "*** REPLY FAILED: written " << ret << " expected "
- << sizeof(hdr) + len << " ***";
- }
-}
-
-static int fuse_reply_entry(struct fuse* fuse, __u64 unique,
- struct node* parent, const char* name, const char* actual_name,
- const char* path)
-{
- struct node* node;
- struct fuse_entry_out out;
- struct stat s;
-
- if (lstat(path, &s) == -1) {
- return -errno;
- }
-
- pthread_mutex_lock(&fuse->global->lock);
- node = acquire_or_create_child_locked(fuse, parent, name, actual_name);
- if (!node) {
- pthread_mutex_unlock(&fuse->global->lock);
- return -ENOMEM;
- }
- memset(&out, 0, sizeof(out));
- attr_from_stat(fuse, &out.attr, &s, node);
- out.attr_valid = 10;
- out.entry_valid = 10;
- out.nodeid = node->nid;
- out.generation = node->gen;
- pthread_mutex_unlock(&fuse->global->lock);
- fuse_reply(fuse, unique, &out, sizeof(out));
- return NO_STATUS;
-}
-
-static int fuse_reply_attr(struct fuse* fuse, __u64 unique, const struct node* node,
- const char* path)
-{
- struct fuse_attr_out out;
- struct stat s;
-
- if (lstat(path, &s) == -1) {
- return -errno;
- }
- memset(&out, 0, sizeof(out));
- attr_from_stat(fuse, &out.attr, &s, node);
- out.attr_valid = 10;
- fuse_reply(fuse, unique, &out, sizeof(out));
- return NO_STATUS;
-}
-
-static void fuse_notify_delete(struct fuse* fuse, const __u64 parent,
- const __u64 child, const char* name) {
- struct fuse_out_header hdr;
- struct fuse_notify_delete_out data;
- size_t namelen = strlen(name);
- hdr.len = sizeof(hdr) + sizeof(data) + namelen + 1;
- hdr.error = FUSE_NOTIFY_DELETE;
- hdr.unique = 0;
-
- data.parent = parent;
- data.child = child;
- data.namelen = namelen;
- data.padding = 0;
-
- struct iovec vec[3];
- vec[0].iov_base = &hdr;
- vec[0].iov_len = sizeof(hdr);
- vec[1].iov_base = &data;
- vec[1].iov_len = sizeof(data);
- vec[2].iov_base = (void*) name;
- vec[2].iov_len = namelen + 1;
-
- ssize_t ret = TEMP_FAILURE_RETRY(writev(fuse->fd, vec, 3));
- /* Ignore ENOENT, since other views may not have seen the entry */
- if (ret == -1) {
- if (errno != ENOENT) {
- PLOG(ERROR) << "*** NOTIFY FAILED ***";
- }
- } else if (static_cast<size_t>(ret) != sizeof(hdr) + sizeof(data) + namelen + 1) {
- LOG(ERROR) << "*** NOTIFY FAILED: written " << ret << " expected "
- << sizeof(hdr) + sizeof(data) + namelen + 1 << " ***";
- }
-}
-
-static int handle_lookup(struct fuse* fuse, struct fuse_handler* handler,
- const struct fuse_in_header *hdr, const char* name)
-{
- struct node* parent_node;
- char parent_path[PATH_MAX];
- char child_path[PATH_MAX];
- const char* actual_name;
-
- pthread_mutex_lock(&fuse->global->lock);
- parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid,
- parent_path, sizeof(parent_path));
- DLOG(INFO) << "[" << handler->token << "] LOOKUP " << name << " @ " << hdr->nodeid
- << " (" << (parent_node ? parent_node->name : "?") << ")";
- pthread_mutex_unlock(&fuse->global->lock);
-
- if (!parent_node || !(actual_name = find_file_within(parent_path, name,
- child_path, sizeof(child_path), 1))) {
- return -ENOENT;
- }
- if (!check_caller_access_to_name(fuse, hdr, parent_node, name, R_OK)) {
- return -EACCES;
- }
-
- return fuse_reply_entry(fuse, hdr->unique, parent_node, name, actual_name, child_path);
-}
-
-static int handle_forget(struct fuse* fuse, struct fuse_handler* handler,
- const struct fuse_in_header *hdr, const struct fuse_forget_in *req)
-{
- struct node* node;
-
- pthread_mutex_lock(&fuse->global->lock);
- node = lookup_node_by_id_locked(fuse, hdr->nodeid);
- DLOG(INFO) << "[" << handler->token << "] FORGET #" << req->nlookup
- << " @ " << std::hex << hdr->nodeid
- << " (" << (node ? node->name : "?") << ")";
- if (node) {
- __u64 n = req->nlookup;
- while (n) {
- n--;
- release_node_locked(node);
- }
- }
- pthread_mutex_unlock(&fuse->global->lock);
- return NO_STATUS; /* no reply */
-}
-
-static int handle_getattr(struct fuse* fuse, struct fuse_handler* handler,
- const struct fuse_in_header *hdr, const struct fuse_getattr_in *req)
-{
- struct node* node;
- char path[PATH_MAX];
-
- pthread_mutex_lock(&fuse->global->lock);
- node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, path, sizeof(path));
- DLOG(INFO) << "[" << handler->token << "] GETATTR flags=" << req->getattr_flags
- << " fh=" << std::hex << req->fh << " @ " << hdr->nodeid << std::dec
- << " (" << (node ? node->name : "?") << ")";
- pthread_mutex_unlock(&fuse->global->lock);
-
- if (!node) {
- return -ENOENT;
- }
- if (!check_caller_access_to_node(fuse, hdr, node, R_OK)) {
- return -EACCES;
- }
-
- return fuse_reply_attr(fuse, hdr->unique, node, path);
-}
-
-static int handle_setattr(struct fuse* fuse, struct fuse_handler* handler,
- const struct fuse_in_header *hdr, const struct fuse_setattr_in *req)
-{
- struct node* node;
- char path[PATH_MAX];
- struct timespec times[2];
-
- pthread_mutex_lock(&fuse->global->lock);
- node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, path, sizeof(path));
- DLOG(INFO) << "[" << handler->token << "] SETATTR fh=" << std::hex << req->fh
- << " valid=" << std::hex << req->valid << " @ " << hdr->nodeid << std::dec
- << " (" << (node ? node->name : "?") << ")";
- pthread_mutex_unlock(&fuse->global->lock);
-
- if (!node) {
- return -ENOENT;
- }
-
- if (!(req->valid & FATTR_FH) &&
- !check_caller_access_to_node(fuse, hdr, node, W_OK)) {
- return -EACCES;
- }
-
- /* XXX: incomplete implementation on purpose.
- * chmod/chown should NEVER be implemented.*/
-
- if ((req->valid & FATTR_SIZE) && TEMP_FAILURE_RETRY(truncate64(path, req->size)) == -1) {
- return -errno;
- }
-
- /* Handle changing atime and mtime. If FATTR_ATIME_and FATTR_ATIME_NOW
- * are both set, then set it to the current time. Else, set it to the
- * time specified in the request. Same goes for mtime. Use utimensat(2)
- * as it allows ATIME and MTIME to be changed independently, and has
- * nanosecond resolution which fuse also has.
- */
- if (req->valid & (FATTR_ATIME | FATTR_MTIME)) {
- times[0].tv_nsec = UTIME_OMIT;
- times[1].tv_nsec = UTIME_OMIT;
- if (req->valid & FATTR_ATIME) {
- if (req->valid & FATTR_ATIME_NOW) {
- times[0].tv_nsec = UTIME_NOW;
- } else {
- times[0].tv_sec = req->atime;
- times[0].tv_nsec = req->atimensec;
- }
- }
- if (req->valid & FATTR_MTIME) {
- if (req->valid & FATTR_MTIME_NOW) {
- times[1].tv_nsec = UTIME_NOW;
- } else {
- times[1].tv_sec = req->mtime;
- times[1].tv_nsec = req->mtimensec;
- }
- }
- DLOG(INFO) << "[" << handler->token << "] Calling utimensat on " << path
- << " with atime " << times[0].tv_sec << ", mtime=" << times[1].tv_sec;
- if (utimensat(-1, path, times, 0) < 0) {
- return -errno;
- }
- }
- return fuse_reply_attr(fuse, hdr->unique, node, path);
-}
-
-static int handle_mknod(struct fuse* fuse, struct fuse_handler* handler,
- const struct fuse_in_header* hdr, const struct fuse_mknod_in* req, const char* name)
-{
- struct node* parent_node;
- char parent_path[PATH_MAX];
- char child_path[PATH_MAX];
- const char* actual_name;
-
- pthread_mutex_lock(&fuse->global->lock);
- parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid,
- parent_path, sizeof(parent_path));
- DLOG(INFO) << "[" << handler->token << "] MKNOD " << name << " 0" << std::oct << req->mode
- << " @ " << std::hex << hdr->nodeid
- << " (" << (parent_node ? parent_node->name : "?") << ")";
- pthread_mutex_unlock(&fuse->global->lock);
-
- if (!parent_node || !(actual_name = find_file_within(parent_path, name,
- child_path, sizeof(child_path), 1))) {
- return -ENOENT;
- }
- if (!check_caller_access_to_name(fuse, hdr, parent_node, name, W_OK)) {
- return -EACCES;
- }
- __u32 mode = (req->mode & (~0777)) | 0664;
- if (mknod(child_path, mode, req->rdev) == -1) {
- return -errno;
- }
- return fuse_reply_entry(fuse, hdr->unique, parent_node, name, actual_name, child_path);
-}
-
-static int handle_mkdir(struct fuse* fuse, struct fuse_handler* handler,
- const struct fuse_in_header* hdr, const struct fuse_mkdir_in* req, const char* name)
-{
- struct node* parent_node;
- char parent_path[PATH_MAX];
- char child_path[PATH_MAX];
- const char* actual_name;
-
- pthread_mutex_lock(&fuse->global->lock);
- parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid,
- parent_path, sizeof(parent_path));
- DLOG(INFO) << "[" << handler->token << "] MKDIR " << name << " 0" << std::oct << req->mode
- << " @ " << std::hex << hdr->nodeid
- << " (" << (parent_node ? parent_node->name : "?") << ")";
- pthread_mutex_unlock(&fuse->global->lock);
-
- if (!parent_node || !(actual_name = find_file_within(parent_path, name,
- child_path, sizeof(child_path), 1))) {
- return -ENOENT;
- }
- if (!check_caller_access_to_name(fuse, hdr, parent_node, name, W_OK)) {
- return -EACCES;
- }
- __u32 mode = (req->mode & (~0777)) | 0775;
- if (mkdir(child_path, mode) == -1) {
- return -errno;
- }
-
- /* When creating /Android/data and /Android/obb, mark them as .nomedia */
- if (parent_node->perm == PERM_ANDROID && !strcasecmp(name, "data")) {
- char nomedia[PATH_MAX];
- snprintf(nomedia, PATH_MAX, "%s/.nomedia", child_path);
- if (touch(nomedia, 0664) != 0) {
- PLOG(ERROR) << "touch(" << nomedia << ") failed";
- return -ENOENT;
- }
- }
- if (parent_node->perm == PERM_ANDROID && !strcasecmp(name, "obb")) {
- char nomedia[PATH_MAX];
- snprintf(nomedia, PATH_MAX, "%s/.nomedia", fuse->global->obb_path);
- if (touch(nomedia, 0664) != 0) {
- PLOG(ERROR) << "touch(" << nomedia << ") failed";
- return -ENOENT;
- }
- }
-
- return fuse_reply_entry(fuse, hdr->unique, parent_node, name, actual_name, child_path);
-}
-
-static int handle_unlink(struct fuse* fuse, struct fuse_handler* handler,
- const struct fuse_in_header* hdr, const char* name)
-{
- struct node* parent_node;
- struct node* child_node;
- char parent_path[PATH_MAX];
- char child_path[PATH_MAX];
-
- pthread_mutex_lock(&fuse->global->lock);
- parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid,
- parent_path, sizeof(parent_path));
- DLOG(INFO) << "[" << handler->token << "] UNLINK " << name << " @ " << std::hex << hdr->nodeid
- << " (" << (parent_node ? parent_node->name : "?") << ")";
- pthread_mutex_unlock(&fuse->global->lock);
-
- if (!parent_node || !find_file_within(parent_path, name,
- child_path, sizeof(child_path), 1)) {
- return -ENOENT;
- }
- if (!check_caller_access_to_name(fuse, hdr, parent_node, name, W_OK)) {
- return -EACCES;
- }
- if (unlink(child_path) == -1) {
- return -errno;
- }
- pthread_mutex_lock(&fuse->global->lock);
- child_node = lookup_child_by_name_locked(parent_node, name);
- if (child_node) {
- child_node->deleted = true;
- }
- pthread_mutex_unlock(&fuse->global->lock);
- if (parent_node && child_node) {
- /* Tell all other views that node is gone */
- DLOG(INFO) << "[" << handler->token << "] fuse_notify_delete"
- << " parent=" << std::hex << parent_node->nid
- << ", child=" << std::hex << child_node->nid << std::dec
- << ", name=" << name;
- if (fuse != fuse->global->fuse_default) {
- fuse_notify_delete(fuse->global->fuse_default, parent_node->nid, child_node->nid, name);
- }
- if (fuse != fuse->global->fuse_read) {
- fuse_notify_delete(fuse->global->fuse_read, parent_node->nid, child_node->nid, name);
- }
- if (fuse != fuse->global->fuse_write) {
- fuse_notify_delete(fuse->global->fuse_write, parent_node->nid, child_node->nid, name);
- }
- }
- return 0;
-}
-
-static int handle_rmdir(struct fuse* fuse, struct fuse_handler* handler,
- const struct fuse_in_header* hdr, const char* name)
-{
- struct node* child_node;
- struct node* parent_node;
- char parent_path[PATH_MAX];
- char child_path[PATH_MAX];
-
- pthread_mutex_lock(&fuse->global->lock);
- parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid,
- parent_path, sizeof(parent_path));
- DLOG(INFO) << "[" << handler->token << "] UNLINK " << name << " @ " << std::hex << hdr->nodeid
- << " (" << (parent_node ? parent_node->name : "?") << ")";
- pthread_mutex_unlock(&fuse->global->lock);
-
- if (!parent_node || !find_file_within(parent_path, name,
- child_path, sizeof(child_path), 1)) {
- return -ENOENT;
- }
- if (!check_caller_access_to_name(fuse, hdr, parent_node, name, W_OK)) {
- return -EACCES;
- }
- if (rmdir(child_path) == -1) {
- return -errno;
- }
- pthread_mutex_lock(&fuse->global->lock);
- child_node = lookup_child_by_name_locked(parent_node, name);
- if (child_node) {
- child_node->deleted = true;
- }
- pthread_mutex_unlock(&fuse->global->lock);
- if (parent_node && child_node) {
- /* Tell all other views that node is gone */
- DLOG(INFO) << "[" << handler->token << "] fuse_notify_delete"
- << " parent=" << std::hex << parent_node->nid
- << ", child=" << std::hex << child_node->nid << std::dec
- << ", name=" << name;
- if (fuse != fuse->global->fuse_default) {
- fuse_notify_delete(fuse->global->fuse_default, parent_node->nid, child_node->nid, name);
- }
- if (fuse != fuse->global->fuse_read) {
- fuse_notify_delete(fuse->global->fuse_read, parent_node->nid, child_node->nid, name);
- }
- if (fuse != fuse->global->fuse_write) {
- fuse_notify_delete(fuse->global->fuse_write, parent_node->nid, child_node->nid, name);
- }
- }
- return 0;
-}
-
-static int handle_rename(struct fuse* fuse, struct fuse_handler* handler,
- const struct fuse_in_header* hdr, const struct fuse_rename_in* req,
- const char* old_name, const char* new_name)
-{
- struct node* old_parent_node;
- struct node* new_parent_node;
- struct node* child_node;
- char old_parent_path[PATH_MAX];
- char new_parent_path[PATH_MAX];
- char old_child_path[PATH_MAX];
- char new_child_path[PATH_MAX];
- const char* new_actual_name;
- int search;
- int res;
-
- pthread_mutex_lock(&fuse->global->lock);
- old_parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid,
- old_parent_path, sizeof(old_parent_path));
- new_parent_node = lookup_node_and_path_by_id_locked(fuse, req->newdir,
- new_parent_path, sizeof(new_parent_path));
- DLOG(INFO) << "[" << handler->token << "] RENAME " << old_name << "->" << new_name
- << " @ " << std::hex << hdr->nodeid
- << " (" << (old_parent_node ? old_parent_node->name : "?") << ") -> "
- << std::hex << req->newdir
- << " (" << (new_parent_node ? new_parent_node->name : "?") << ")";
- if (!old_parent_node || !new_parent_node) {
- res = -ENOENT;
- goto lookup_error;
- }
- if (!check_caller_access_to_name(fuse, hdr, old_parent_node, old_name, W_OK)) {
- res = -EACCES;
- goto lookup_error;
- }
- if (!check_caller_access_to_name(fuse, hdr, new_parent_node, new_name, W_OK)) {
- res = -EACCES;
- goto lookup_error;
- }
- child_node = lookup_child_by_name_locked(old_parent_node, old_name);
- if (!child_node || get_node_path_locked(child_node,
- old_child_path, sizeof(old_child_path)) < 0) {
- res = -ENOENT;
- goto lookup_error;
- }
- acquire_node_locked(child_node);
- pthread_mutex_unlock(&fuse->global->lock);
-
- /* Special case for renaming a file where destination is same path
- * differing only by case. In this case we don't want to look for a case
- * insensitive match. This allows commands like "mv foo FOO" to work as expected.
- */
- search = old_parent_node != new_parent_node
- || strcasecmp(old_name, new_name);
- if (!(new_actual_name = find_file_within(new_parent_path, new_name,
- new_child_path, sizeof(new_child_path), search))) {
- res = -ENOENT;
- goto io_error;
- }
-
- DLOG(INFO) << "[" << handler->token << "] RENAME " << old_child_path << "->" << new_child_path;
- res = rename(old_child_path, new_child_path);
- if (res == -1) {
- res = -errno;
- goto io_error;
- }
-
- pthread_mutex_lock(&fuse->global->lock);
- res = rename_node_locked(child_node, new_name, new_actual_name);
- if (!res) {
- remove_node_from_parent_locked(child_node);
- derive_permissions_locked(fuse, new_parent_node, child_node);
- derive_permissions_recursive_locked(fuse, child_node);
- add_node_to_parent_locked(child_node, new_parent_node);
- }
- goto done;
-
-io_error:
- pthread_mutex_lock(&fuse->global->lock);
-done:
- release_node_locked(child_node);
-lookup_error:
- pthread_mutex_unlock(&fuse->global->lock);
- return res;
-}
-
-static int open_flags_to_access_mode(int open_flags) {
- if ((open_flags & O_ACCMODE) == O_RDONLY) {
- return R_OK;
- } else if ((open_flags & O_ACCMODE) == O_WRONLY) {
- return W_OK;
- } else {
- /* Probably O_RDRW, but treat as default to be safe */
- return R_OK | W_OK;
- }
-}
-
-static int handle_open(struct fuse* fuse, struct fuse_handler* handler,
- const struct fuse_in_header* hdr, const struct fuse_open_in* req)
-{
- struct node* node;
- char path[PATH_MAX];
- struct fuse_open_out out = {};
- struct handle *h;
-
- pthread_mutex_lock(&fuse->global->lock);
- node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, path, sizeof(path));
- DLOG(INFO) << "[" << handler->token << "] OPEN 0" << std::oct << req->flags
- << " @ " << std::hex << hdr->nodeid << std::dec
- << " (" << (node ? node->name : "?") << ")";
- pthread_mutex_unlock(&fuse->global->lock);
-
- if (!node) {
- return -ENOENT;
- }
- if (!check_caller_access_to_node(fuse, hdr, node,
- open_flags_to_access_mode(req->flags))) {
- return -EACCES;
- }
- h = static_cast<struct handle*>(malloc(sizeof(*h)));
- if (!h) {
- return -ENOMEM;
- }
- DLOG(INFO) << "[" << handler->token << "] OPEN " << path;
- h->fd = TEMP_FAILURE_RETRY(open(path, req->flags));
- if (h->fd == -1) {
- free(h);
- return -errno;
- }
- out.fh = ptr_to_id(h);
- out.open_flags = 0;
- fuse_reply(fuse, hdr->unique, &out, sizeof(out));
- return NO_STATUS;
-}
-
-static int handle_read(struct fuse* fuse, struct fuse_handler* handler,
- const struct fuse_in_header* hdr, const struct fuse_read_in* req)
-{
- struct handle *h = static_cast<struct handle*>(id_to_ptr(req->fh));
- __u64 unique = hdr->unique;
- __u32 size = req->size;
- __u64 offset = req->offset;
- int res;
- __u8 *read_buffer = (__u8 *) ((uintptr_t)(handler->read_buffer + PAGE_SIZE) & ~((uintptr_t)PAGE_SIZE-1));
-
- /* Don't access any other fields of hdr or req beyond this point, the read buffer
- * overlaps the request buffer and will clobber data in the request. This
- * saves us 128KB per request handler thread at the cost of this scary comment. */
-
- DLOG(INFO) << "[" << handler->token << "] READ " << std::hex << h << std::dec
- << "(" << h->fd << ") " << size << "@" << offset;
- if (size > MAX_READ) {
- return -EINVAL;
- }
- res = TEMP_FAILURE_RETRY(pread64(h->fd, read_buffer, size, offset));
- if (res == -1) {
- return -errno;
- }
- fuse_reply(fuse, unique, read_buffer, res);
- return NO_STATUS;
-}
-
-static int handle_write(struct fuse* fuse, struct fuse_handler* handler,
- const struct fuse_in_header* hdr, const struct fuse_write_in* req,
- const void* buffer)
-{
- struct fuse_write_out out;
- struct handle *h = static_cast<struct handle*>(id_to_ptr(req->fh));
- int res;
- __u8 aligned_buffer[req->size] __attribute__((__aligned__(PAGE_SIZE)));
-
- if (req->flags & O_DIRECT) {
- memcpy(aligned_buffer, buffer, req->size);
- buffer = (const __u8*) aligned_buffer;
- }
-
- DLOG(INFO) << "[" << handler->token << "] WRITE " << std::hex << h << std::dec
- << "(" << h->fd << ") " << req->size << "@" << req->offset;
- res = TEMP_FAILURE_RETRY(pwrite64(h->fd, buffer, req->size, req->offset));
- if (res == -1) {
- return -errno;
- }
- out.size = res;
- out.padding = 0;
- fuse_reply(fuse, hdr->unique, &out, sizeof(out));
- return NO_STATUS;
-}
-
-static int handle_statfs(struct fuse* fuse, struct fuse_handler* handler,
- const struct fuse_in_header* hdr)
-{
- char path[PATH_MAX];
- struct statfs stat;
- struct fuse_statfs_out out;
- int res;
-
- pthread_mutex_lock(&fuse->global->lock);
- DLOG(INFO) << "[" << handler->token << "] STATFS";
- res = get_node_path_locked(&fuse->global->root, path, sizeof(path));
- pthread_mutex_unlock(&fuse->global->lock);
- if (res < 0) {
- return -ENOENT;
- }
- if (TEMP_FAILURE_RETRY(statfs(fuse->global->root.name, &stat)) == -1) {
- return -errno;
- }
- memset(&out, 0, sizeof(out));
- out.st.blocks = stat.f_blocks;
- out.st.bfree = stat.f_bfree;
- out.st.bavail = stat.f_bavail;
- out.st.files = stat.f_files;
- out.st.ffree = stat.f_ffree;
- out.st.bsize = stat.f_bsize;
- out.st.namelen = stat.f_namelen;
- out.st.frsize = stat.f_frsize;
- fuse_reply(fuse, hdr->unique, &out, sizeof(out));
- return NO_STATUS;
-}
-
-static int handle_release(struct fuse* fuse, struct fuse_handler* handler,
- const struct fuse_in_header* hdr, const struct fuse_release_in* req)
-{
- struct handle *h = static_cast<struct handle*>(id_to_ptr(req->fh));
-
- DLOG(INFO) << "[" << handler->token << "] RELEASE " << std::hex << h << std::dec
- << "(" << h->fd << ")";
- close(h->fd);
- free(h);
- return 0;
-}
-
-static int handle_fsync(struct fuse* fuse, struct fuse_handler* handler,
- const struct fuse_in_header* hdr, const struct fuse_fsync_in* req)
-{
- bool is_dir = (hdr->opcode == FUSE_FSYNCDIR);
- bool is_data_sync = req->fsync_flags & 1;
-
- int fd = -1;
- if (is_dir) {
- struct dirhandle *dh = static_cast<struct dirhandle*>(id_to_ptr(req->fh));
- fd = dirfd(dh->d);
- } else {
- struct handle *h = static_cast<struct handle*>(id_to_ptr(req->fh));
- fd = h->fd;
- }
-
- DLOG(INFO) << "[" << handler->token << "] " << (is_dir ? "FSYNCDIR" : "FSYNC") << " "
- << std::hex << req->fh << std::dec << "(" << fd << ") is_data_sync=" << is_data_sync;
- int res = is_data_sync ? fdatasync(fd) : fsync(fd);
- if (res == -1) {
- return -errno;
- }
- return 0;
-}
-
-static int handle_flush(struct fuse* fuse, struct fuse_handler* handler,
- const struct fuse_in_header* hdr)
-{
- DLOG(INFO) << "[" << handler->token << "] FLUSH";
- return 0;
-}
-
-static int handle_opendir(struct fuse* fuse, struct fuse_handler* handler,
- const struct fuse_in_header* hdr, const struct fuse_open_in* req)
-{
- struct node* node;
- char path[PATH_MAX];
- struct fuse_open_out out = {};
- struct dirhandle *h;
-
- pthread_mutex_lock(&fuse->global->lock);
- node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, path, sizeof(path));
- DLOG(INFO) << "[" << handler->token << "] OPENDIR @ " << std::hex << hdr->nodeid
- << " (" << (node ? node->name : "?") << ")";
- pthread_mutex_unlock(&fuse->global->lock);
-
- if (!node) {
- return -ENOENT;
- }
- if (!check_caller_access_to_node(fuse, hdr, node, R_OK)) {
- return -EACCES;
- }
- h = static_cast<struct dirhandle*>(malloc(sizeof(*h)));
- if (!h) {
- return -ENOMEM;
- }
- DLOG(INFO) << "[" << handler->token << "] OPENDIR " << path;
- h->d = opendir(path);
- if (!h->d) {
- free(h);
- return -errno;
- }
- out.fh = ptr_to_id(h);
- out.open_flags = 0;
- fuse_reply(fuse, hdr->unique, &out, sizeof(out));
- return NO_STATUS;
-}
-
-static int handle_readdir(struct fuse* fuse, struct fuse_handler* handler,
- const struct fuse_in_header* hdr, const struct fuse_read_in* req)
-{
- char buffer[8192];
- struct fuse_dirent *fde = (struct fuse_dirent*) buffer;
- struct dirent *de;
- struct dirhandle *h = static_cast<struct dirhandle*>(id_to_ptr(req->fh));
-
- DLOG(INFO) << "[" << handler->token << "] READDIR " << h;
- if (req->offset == 0) {
- /* rewinddir() might have been called above us, so rewind here too */
- DLOG(INFO) << "[" << handler->token << "] calling rewinddir()";
- rewinddir(h->d);
- }
- de = readdir(h->d);
- if (!de) {
- return 0;
- }
- fde->ino = FUSE_UNKNOWN_INO;
- /* increment the offset so we can detect when rewinddir() seeks back to the beginning */
- fde->off = req->offset + 1;
- fde->type = de->d_type;
- fde->namelen = strlen(de->d_name);
- memcpy(fde->name, de->d_name, fde->namelen + 1);
- fuse_reply(fuse, hdr->unique, fde,
- FUSE_DIRENT_ALIGN(sizeof(struct fuse_dirent) + fde->namelen));
- return NO_STATUS;
-}
-
-static int handle_releasedir(struct fuse* fuse, struct fuse_handler* handler,
- const struct fuse_in_header* hdr, const struct fuse_release_in* req)
-{
- struct dirhandle *h = static_cast<struct dirhandle*>(id_to_ptr(req->fh));
-
- DLOG(INFO) << "[" << handler->token << "] RELEASEDIR " << h;
- closedir(h->d);
- free(h);
- return 0;
-}
-
-static int handle_init(struct fuse* fuse, struct fuse_handler* handler,
- const struct fuse_in_header* hdr, const struct fuse_init_in* req)
-{
- struct fuse_init_out out;
- size_t fuse_struct_size;
-
- DLOG(INFO) << "[" << handler->token << "] INIT ver=" << req->major << "." << req->minor
- << " maxread=" << req->max_readahead << " flags=" << std::hex << req->flags;
-
- /* Kernel 2.6.16 is the first stable kernel with struct fuse_init_out
- * defined (fuse version 7.6). The structure is the same from 7.6 through
- * 7.22. Beginning with 7.23, the structure increased in size and added
- * new parameters.
- */
- if (req->major != FUSE_KERNEL_VERSION || req->minor < 6) {
- LOG(ERROR) << "Fuse kernel version mismatch: Kernel version "
- << req->major << "." << req->minor
- << ", Expected at least " << FUSE_KERNEL_VERSION << ".6";
- return -1;
- }
-
- /* We limit ourselves to 15 because we don't handle BATCH_FORGET yet */
- out.minor = MIN(req->minor, 15);
- fuse_struct_size = sizeof(out);
-#if defined(FUSE_COMPAT_22_INIT_OUT_SIZE)
- /* FUSE_KERNEL_VERSION >= 23. */
-
- /* Since we return minor version 15, the kernel does not accept the latest
- * fuse_init_out size. We need to use FUSE_COMPAT_22_INIT_OUT_SIZE always.*/
- fuse_struct_size = FUSE_COMPAT_22_INIT_OUT_SIZE;
-#endif
-
- out.major = FUSE_KERNEL_VERSION;
- out.max_readahead = req->max_readahead;
- out.flags = FUSE_ATOMIC_O_TRUNC | FUSE_BIG_WRITES;
- out.max_background = 32;
- out.congestion_threshold = 32;
- out.max_write = MAX_WRITE;
- fuse_reply(fuse, hdr->unique, &out, fuse_struct_size);
- return NO_STATUS;
-}
-
-static int handle_canonical_path(struct fuse* fuse, struct fuse_handler* handler,
- const struct fuse_in_header *hdr)
-{
- struct node* node;
- char path[PATH_MAX];
- int len;
-
- pthread_mutex_lock(&fuse->global->lock);
- node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid,
- path, sizeof(path));
- DLOG(INFO) << "[" << handler->token << "] CANONICAL_PATH @ " << std::hex << hdr->nodeid
- << std::dec << " (" << (node ? node->name : "?") << ")";
- pthread_mutex_unlock(&fuse->global->lock);
-
- if (!node) {
- return -ENOENT;
- }
- if (!check_caller_access_to_node(fuse, hdr, node, R_OK)) {
- return -EACCES;
- }
- len = strlen(path);
- if (len + 1 > PATH_MAX)
- len = PATH_MAX - 1;
- path[PATH_MAX - 1] = 0;
- fuse_reply(fuse, hdr->unique, path, len + 1);
- return NO_STATUS;
-}
-
-static int handle_fuse_request(struct fuse *fuse, struct fuse_handler* handler,
- const struct fuse_in_header *hdr, const void *data, size_t data_len)
-{
- switch (hdr->opcode) {
- case FUSE_LOOKUP: { /* bytez[] -> entry_out */
- const char *name = static_cast<const char*>(data);
- return handle_lookup(fuse, handler, hdr, name);
- }
-
- case FUSE_FORGET: {
- const struct fuse_forget_in *req = static_cast<const struct fuse_forget_in*>(data);
- return handle_forget(fuse, handler, hdr, req);
- }
-
- case FUSE_GETATTR: { /* getattr_in -> attr_out */
- const struct fuse_getattr_in *req = static_cast<const struct fuse_getattr_in*>(data);
- return handle_getattr(fuse, handler, hdr, req);
- }
-
- case FUSE_SETATTR: { /* setattr_in -> attr_out */
- const struct fuse_setattr_in *req = static_cast<const struct fuse_setattr_in*>(data);
- return handle_setattr(fuse, handler, hdr, req);
- }
-
-// case FUSE_READLINK:
-// case FUSE_SYMLINK:
- case FUSE_MKNOD: { /* mknod_in, bytez[] -> entry_out */
- const struct fuse_mknod_in *req = static_cast<const struct fuse_mknod_in*>(data);
- const char *name = ((const char*) data) + sizeof(*req);
- return handle_mknod(fuse, handler, hdr, req, name);
- }
-
- case FUSE_MKDIR: { /* mkdir_in, bytez[] -> entry_out */
- const struct fuse_mkdir_in *req = static_cast<const struct fuse_mkdir_in*>(data);
- const char *name = ((const char*) data) + sizeof(*req);
- return handle_mkdir(fuse, handler, hdr, req, name);
- }
-
- case FUSE_UNLINK: { /* bytez[] -> */
- const char *name = static_cast<const char*>(data);
- return handle_unlink(fuse, handler, hdr, name);
- }
-
- case FUSE_RMDIR: { /* bytez[] -> */
- const char *name = static_cast<const char*>(data);
- return handle_rmdir(fuse, handler, hdr, name);
- }
-
- case FUSE_RENAME: { /* rename_in, oldname, newname -> */
- const struct fuse_rename_in *req = static_cast<const struct fuse_rename_in*>(data);
- const char *old_name = ((const char*) data) + sizeof(*req);
- const char *new_name = old_name + strlen(old_name) + 1;
- return handle_rename(fuse, handler, hdr, req, old_name, new_name);
- }
-
-// case FUSE_LINK:
- case FUSE_OPEN: { /* open_in -> open_out */
- const struct fuse_open_in *req = static_cast<const struct fuse_open_in*>(data);
- return handle_open(fuse, handler, hdr, req);
- }
-
- case FUSE_READ: { /* read_in -> byte[] */
- const struct fuse_read_in *req = static_cast<const struct fuse_read_in*>(data);
- return handle_read(fuse, handler, hdr, req);
- }
-
- case FUSE_WRITE: { /* write_in, byte[write_in.size] -> write_out */
- const struct fuse_write_in *req = static_cast<const struct fuse_write_in*>(data);
- const void* buffer = (const __u8*)data + sizeof(*req);
- return handle_write(fuse, handler, hdr, req, buffer);
- }
-
- case FUSE_STATFS: { /* getattr_in -> attr_out */
- return handle_statfs(fuse, handler, hdr);
- }
-
- case FUSE_RELEASE: { /* release_in -> */
- const struct fuse_release_in *req = static_cast<const struct fuse_release_in*>(data);
- return handle_release(fuse, handler, hdr, req);
- }
-
- case FUSE_FSYNC:
- case FUSE_FSYNCDIR: {
- const struct fuse_fsync_in *req = static_cast<const struct fuse_fsync_in*>(data);
- return handle_fsync(fuse, handler, hdr, req);
- }
-
-// case FUSE_SETXATTR:
-// case FUSE_GETXATTR:
-// case FUSE_LISTXATTR:
-// case FUSE_REMOVEXATTR:
- case FUSE_FLUSH: {
- return handle_flush(fuse, handler, hdr);
- }
-
- case FUSE_OPENDIR: { /* open_in -> open_out */
- const struct fuse_open_in *req = static_cast<const struct fuse_open_in*>(data);
- return handle_opendir(fuse, handler, hdr, req);
- }
-
- case FUSE_READDIR: {
- const struct fuse_read_in *req = static_cast<const struct fuse_read_in*>(data);
- return handle_readdir(fuse, handler, hdr, req);
- }
-
- case FUSE_RELEASEDIR: { /* release_in -> */
- const struct fuse_release_in *req = static_cast<const struct fuse_release_in*>(data);
- return handle_releasedir(fuse, handler, hdr, req);
- }
-
- case FUSE_INIT: { /* init_in -> init_out */
- const struct fuse_init_in *req = static_cast<const struct fuse_init_in*>(data);
- return handle_init(fuse, handler, hdr, req);
- }
-
- case FUSE_CANONICAL_PATH: { /* nodeid -> bytez[] */
- return handle_canonical_path(fuse, handler, hdr);
- }
-
- default: {
- DLOG(INFO) << "[" << handler->token << "] NOTIMPL op=" << hdr->opcode
- << "uniq=" << std::hex << hdr->unique << "nid=" << hdr->nodeid << std::dec;
- return -ENOSYS;
- }
- }
-}
-
-void handle_fuse_requests(struct fuse_handler* handler)
-{
- struct fuse* fuse = handler->fuse;
- for (;;) {
- ssize_t len = TEMP_FAILURE_RETRY(read(fuse->fd,
- handler->request_buffer, sizeof(handler->request_buffer)));
- if (len == -1) {
- if (errno == ENODEV) {
- LOG(ERROR) << "[" << handler->token << "] someone stole our marbles!";
- exit(2);
- }
- PLOG(ERROR) << "[" << handler->token << "] handle_fuse_requests";
- continue;
- }
-
- if (static_cast<size_t>(len) < sizeof(struct fuse_in_header)) {
- LOG(ERROR) << "[" << handler->token << "] request too short: len=" << len;
- continue;
- }
-
- const struct fuse_in_header* hdr =
- reinterpret_cast<const struct fuse_in_header*>(handler->request_buffer);
- if (hdr->len != static_cast<size_t>(len)) {
- LOG(ERROR) << "[" << handler->token << "] malformed header: len=" << len
- << ", hdr->len=" << hdr->len;
- continue;
- }
-
- const void *data = handler->request_buffer + sizeof(struct fuse_in_header);
- size_t data_len = len - sizeof(struct fuse_in_header);
- __u64 unique = hdr->unique;
- int res = handle_fuse_request(fuse, handler, hdr, data, data_len);
-
- /* We do not access the request again after this point because the underlying
- * buffer storage may have been reused while processing the request. */
-
- if (res != NO_STATUS) {
- if (res) {
- DLOG(INFO) << "[" << handler->token << "] ERROR " << res;
- }
- fuse_status(fuse, unique, res);
- }
- }
-}
diff --git a/sdcard/fuse.h b/sdcard/fuse.h
deleted file mode 100644
index 9ccd21d..0000000
--- a/sdcard/fuse.h
+++ /dev/null
@@ -1,209 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef FUSE_H_
-#define FUSE_H_
-
-#include <dirent.h>
-#include <fcntl.h>
-#include <linux/fuse.h>
-#include <pthread.h>
-#include <stdbool.h>
-#include <stdlib.h>
-#include <sys/param.h>
-#include <sys/stat.h>
-#include <sys/statfs.h>
-#include <sys/types.h>
-#include <sys/uio.h>
-#include <unistd.h>
-
-#include <map>
-#include <string>
-
-#include <android-base/logging.h>
-#include <cutils/fs.h>
-#include <cutils/multiuser.h>
-#include <packagelistparser/packagelistparser.h>
-
-#include <private/android_filesystem_config.h>
-
-#define FUSE_TRACE 0
-
-#if FUSE_TRACE
-static constexpr bool kEnableDLog = true;
-#else // FUSE_TRACE == 0
-static constexpr bool kEnableDLog = false;
-#endif
-
-// Use same strategy as DCHECK().
-#define DLOG(x) \
- if (kEnableDLog) LOG(x)
-
-/* Maximum number of bytes to write in one request. */
-#define MAX_WRITE (256 * 1024)
-
-/* Maximum number of bytes to read in one request. */
-#define MAX_READ (128 * 1024)
-
-/* Largest possible request.
- * The request size is bounded by the maximum size of a FUSE_WRITE request because it has
- * the largest possible data payload. */
-#define MAX_REQUEST_SIZE (sizeof(struct fuse_in_header) + sizeof(struct fuse_write_in) + MAX_WRITE)
-
-namespace {
-struct CaseInsensitiveCompare {
- bool operator()(const std::string& lhs, const std::string& rhs) const {
- return strcasecmp(lhs.c_str(), rhs.c_str()) < 0;
- }
-};
-}
-
-using AppIdMap = std::map<std::string, appid_t, CaseInsensitiveCompare>;
-
-/* Permission mode for a specific node. Controls how file permissions
- * are derived for children nodes. */
-typedef enum {
- /* Nothing special; this node should just inherit from its parent. */
- PERM_INHERIT,
- /* This node is one level above a normal root; used for legacy layouts
- * which use the first level to represent user_id. */
- PERM_PRE_ROOT,
- /* This node is "/" */
- PERM_ROOT,
- /* This node is "/Android" */
- PERM_ANDROID,
- /* This node is "/Android/data" */
- PERM_ANDROID_DATA,
- /* This node is "/Android/obb" */
- PERM_ANDROID_OBB,
- /* This node is "/Android/media" */
- PERM_ANDROID_MEDIA,
-} perm_t;
-
-struct handle {
- int fd;
-};
-
-struct dirhandle {
- DIR *d;
-};
-
-struct node {
- __u32 refcount;
- __u64 nid;
- __u64 gen;
- /*
- * The inode number for this FUSE node. Note that this isn't stable across
- * multiple invocations of the FUSE daemon.
- */
- __u32 ino;
-
- /* State derived based on current position in hierarchy. */
- perm_t perm;
- userid_t userid;
- uid_t uid;
- bool under_android;
-
- struct node *next; /* per-dir sibling list */
- struct node *child; /* first contained file by this dir */
- struct node *parent; /* containing directory */
-
- size_t namelen;
- char *name;
- /* If non-null, this is the real name of the file in the underlying storage.
- * This may differ from the field "name" only by case.
- * strlen(actual_name) will always equal strlen(name), so it is safe to use
- * namelen for both fields.
- */
- char *actual_name;
-
- /* If non-null, an exact underlying path that should be grafted into this
- * position. Used to support things like OBB. */
- char* graft_path;
- size_t graft_pathlen;
-
- bool deleted;
-};
-
-/* Global data for all FUSE mounts */
-struct fuse_global {
- pthread_mutex_t lock;
-
- uid_t uid;
- gid_t gid;
- bool multi_user;
-
- char source_path[PATH_MAX];
- char obb_path[PATH_MAX];
-
- AppIdMap* package_to_appid;
-
- __u64 next_generation;
- struct node root;
-
- /* Used to allocate unique inode numbers for fuse nodes. We use
- * a simple counter based scheme where inode numbers from deleted
- * nodes aren't reused. Note that inode allocations are not stable
- * across multiple invocation of the sdcard daemon, but that shouldn't
- * be a huge problem in practice.
- *
- * Note that we restrict inodes to 32 bit unsigned integers to prevent
- * truncation on 32 bit processes when unsigned long long stat.st_ino is
- * assigned to an unsigned long ino_t type in an LP32 process.
- *
- * Also note that fuse_attr and fuse_dirent inode values are 64 bits wide
- * on both LP32 and LP64, but the fuse kernel code doesn't squash 64 bit
- * inode numbers into 32 bit values on 64 bit kernels (see fuse_squash_ino
- * in fs/fuse/inode.c).
- *
- * Accesses must be guarded by |lock|.
- */
- __u32 inode_ctr;
-
- struct fuse* fuse_default;
- struct fuse* fuse_read;
- struct fuse* fuse_write;
-};
-
-/* Single FUSE mount */
-struct fuse {
- struct fuse_global* global;
-
- char dest_path[PATH_MAX];
-
- int fd;
-
- gid_t gid;
- mode_t mask;
-};
-
-/* Private data used by a single FUSE handler */
-struct fuse_handler {
- struct fuse* fuse;
- int token;
-
- /* To save memory, we never use the contents of the request buffer and the read
- * buffer at the same time. This allows us to share the underlying storage. */
- union {
- __u8 request_buffer[MAX_REQUEST_SIZE];
- __u8 read_buffer[MAX_READ + PAGE_SIZE];
- };
-};
-
-void handle_fuse_requests(struct fuse_handler* handler);
-void derive_permissions_recursive_locked(struct fuse* fuse, struct node *parent);
-
-#endif /* FUSE_H_ */
diff --git a/sdcard/sdcard.cpp b/sdcard/sdcard.cpp
index 77d5a91..574bbfe 100644
--- a/sdcard/sdcard.cpp
+++ b/sdcard/sdcard.cpp
@@ -38,164 +38,18 @@
#include <cutils/multiuser.h>
#include <cutils/properties.h>
-#include <packagelistparser/packagelistparser.h>
-
#include <libminijail.h>
#include <scoped_minijail.h>
#include <private/android_filesystem_config.h>
-// README
-//
-// What is this?
-//
-// sdcard is a program that uses FUSE to emulate FAT-on-sdcard style
-// directory permissions (all files are given fixed owner, group, and
-// permissions at creation, owner, group, and permissions are not
-// changeable, symlinks and hardlinks are not createable, etc.
-//
-// See usage() for command line options.
-//
-// It must be run as root, but will drop to requested UID/GID as soon as it
-// mounts a filesystem. It will refuse to run if requested UID/GID are zero.
-//
-// Things I believe to be true:
-//
-// - ops that return a fuse_entry (LOOKUP, MKNOD, MKDIR, LINK, SYMLINK,
-// CREAT) must bump that node's refcount
-// - don't forget that FORGET can forget multiple references (req->nlookup)
-// - if an op that returns a fuse_entry fails writing the reply to the
-// kernel, you must rollback the refcount to reflect the reference the
-// kernel did not actually acquire
-//
-// This daemon can also derive custom filesystem permissions based on directory
-// structure when requested. These custom permissions support several features:
-//
-// - Apps can access their own files in /Android/data/com.example/ without
-// requiring any additional GIDs.
-// - Separate permissions for protecting directories like Pictures and Music.
-// - Multi-user separation on the same physical device.
-
-#include "fuse.h"
-
-#define PROP_SDCARDFS_DEVICE "ro.sys.sdcardfs"
-#define PROP_SDCARDFS_USER "persist.sys.sdcardfs"
+// NOTE: This is a vestigial program that simply exists to mount the in-kernel
+// sdcardfs filesystem. The older FUSE-based design that used to live here has
+// been completely removed to avoid confusion.
/* Supplementary groups to execute with. */
static const gid_t kGroups[1] = { AID_PACKAGE_INFO };
-static bool package_parse_callback(pkg_info *info, void *userdata) {
- struct fuse_global *global = (struct fuse_global *)userdata;
- bool res = global->package_to_appid->emplace(info->name, info->uid).second;
- packagelist_free(info);
- return res;
-}
-
-static bool read_package_list(struct fuse_global* global) {
- pthread_mutex_lock(&global->lock);
-
- global->package_to_appid->clear();
- bool rc = packagelist_parse(package_parse_callback, global);
- DLOG(INFO) << "read_package_list: found " << global->package_to_appid->size() << " packages";
-
- // Regenerate ownership details using newly loaded mapping.
- derive_permissions_recursive_locked(global->fuse_default, &global->root);
-
- pthread_mutex_unlock(&global->lock);
-
- return rc;
-}
-
-static void watch_package_list(struct fuse_global* global) {
- struct inotify_event *event;
- char event_buf[512];
-
- int nfd = inotify_init();
- if (nfd == -1) {
- PLOG(ERROR) << "inotify_init failed";
- return;
- }
-
- bool active = false;
- while (1) {
- if (!active) {
- int res = inotify_add_watch(nfd, PACKAGES_LIST_FILE, IN_DELETE_SELF);
- if (res == -1) {
- if (errno == ENOENT || errno == EACCES) {
- /* Framework may not have created the file yet, sleep and retry. */
- LOG(ERROR) << "missing \"" << PACKAGES_LIST_FILE << "\"; retrying...";
- sleep(3);
- continue;
- } else {
- PLOG(ERROR) << "inotify_add_watch failed";
- return;
- }
- }
-
- /* Watch above will tell us about any future changes, so
- * read the current state. */
- if (read_package_list(global) == false) {
- LOG(ERROR) << "read_package_list failed";
- return;
- }
- active = true;
- }
-
- int event_pos = 0;
- ssize_t res = TEMP_FAILURE_RETRY(read(nfd, event_buf, sizeof(event_buf)));
- if (res == -1) {
- PLOG(ERROR) << "failed to read inotify event";
- return;
- } else if (static_cast<size_t>(res) < sizeof(*event)) {
- LOG(ERROR) << "failed to read inotify event: read " << res << " expected "
- << sizeof(event_buf);
- return;
- }
-
- while (res >= static_cast<ssize_t>(sizeof(*event))) {
- int event_size;
- event = reinterpret_cast<struct inotify_event*>(event_buf + event_pos);
-
- DLOG(INFO) << "inotify event: " << std::hex << event->mask << std::dec;
- if ((event->mask & IN_IGNORED) == IN_IGNORED) {
- /* Previously watched file was deleted, probably due to move
- * that swapped in new data; re-arm the watch and read. */
- active = false;
- }
-
- event_size = sizeof(*event) + event->len;
- res -= event_size;
- event_pos += event_size;
- }
- }
-}
-
-static int fuse_setup(struct fuse* fuse, gid_t gid, mode_t mask) {
- char opts[256];
-
- fuse->fd = TEMP_FAILURE_RETRY(open("/dev/fuse", O_RDWR | O_CLOEXEC));
- if (fuse->fd == -1) {
- PLOG(ERROR) << "failed to open fuse device";
- return -1;
- }
-
- umount2(fuse->dest_path, MNT_DETACH);
-
- snprintf(opts, sizeof(opts),
- "fd=%i,rootmode=40000,default_permissions,allow_other,user_id=%d,group_id=%d",
- fuse->fd, fuse->global->uid, fuse->global->gid);
- if (mount("/dev/fuse", fuse->dest_path, "fuse", MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_NOATIME,
- opts) == -1) {
- PLOG(ERROR) << "failed to mount fuse filesystem";
- return -1;
- }
-
- fuse->gid = gid;
- fuse->mask = mask;
-
- return 0;
-}
-
static void drop_privs(uid_t uid, gid_t gid) {
ScopedMinijail j(minijail_new());
minijail_set_supplementary_gids(j.get(), arraysize(kGroups), kGroups);
@@ -205,146 +59,28 @@
minijail_enter(j.get());
}
-static void* start_handler(void* data) {
- struct fuse_handler* handler = static_cast<fuse_handler*>(data);
- handle_fuse_requests(handler);
- return NULL;
-}
-
-static void run(const char* source_path, const char* label, uid_t uid,
- gid_t gid, userid_t userid, bool multi_user, bool full_write) {
- struct fuse_global global;
- struct fuse fuse_default;
- struct fuse fuse_read;
- struct fuse fuse_write;
- struct fuse_handler handler_default;
- struct fuse_handler handler_read;
- struct fuse_handler handler_write;
- pthread_t thread_default;
- pthread_t thread_read;
- pthread_t thread_write;
-
- memset(&global, 0, sizeof(global));
- memset(&fuse_default, 0, sizeof(fuse_default));
- memset(&fuse_read, 0, sizeof(fuse_read));
- memset(&fuse_write, 0, sizeof(fuse_write));
- memset(&handler_default, 0, sizeof(handler_default));
- memset(&handler_read, 0, sizeof(handler_read));
- memset(&handler_write, 0, sizeof(handler_write));
-
- pthread_mutex_init(&global.lock, NULL);
- global.package_to_appid = new AppIdMap;
- global.uid = uid;
- global.gid = gid;
- global.multi_user = multi_user;
- global.next_generation = 0;
- global.inode_ctr = 1;
-
- memset(&global.root, 0, sizeof(global.root));
- global.root.nid = FUSE_ROOT_ID; /* 1 */
- global.root.refcount = 2;
- global.root.namelen = strlen(source_path);
- global.root.name = strdup(source_path);
- global.root.userid = userid;
- global.root.uid = AID_ROOT;
- global.root.under_android = false;
-
- // Clang static analyzer think strcpy potentially overwrites other fields
- // in global. Use snprintf() to mute the false warning.
- snprintf(global.source_path, sizeof(global.source_path), "%s", source_path);
-
- if (multi_user) {
- global.root.perm = PERM_PRE_ROOT;
- snprintf(global.obb_path, sizeof(global.obb_path), "%s/obb", source_path);
- } else {
- global.root.perm = PERM_ROOT;
- snprintf(global.obb_path, sizeof(global.obb_path), "%s/Android/obb", source_path);
- }
-
- fuse_default.global = &global;
- fuse_read.global = &global;
- fuse_write.global = &global;
-
- global.fuse_default = &fuse_default;
- global.fuse_read = &fuse_read;
- global.fuse_write = &fuse_write;
-
- snprintf(fuse_default.dest_path, PATH_MAX, "/mnt/runtime/default/%s", label);
- snprintf(fuse_read.dest_path, PATH_MAX, "/mnt/runtime/read/%s", label);
- snprintf(fuse_write.dest_path, PATH_MAX, "/mnt/runtime/write/%s", label);
-
- handler_default.fuse = &fuse_default;
- handler_read.fuse = &fuse_read;
- handler_write.fuse = &fuse_write;
-
- handler_default.token = 0;
- handler_read.token = 1;
- handler_write.token = 2;
-
- umask(0);
-
- if (multi_user) {
- /* Multi-user storage is fully isolated per user, so "other"
- * permissions are completely masked off. */
- if (fuse_setup(&fuse_default, AID_SDCARD_RW, 0006)
- || fuse_setup(&fuse_read, AID_EVERYBODY, 0027)
- || fuse_setup(&fuse_write, AID_EVERYBODY, full_write ? 0007 : 0027)) {
- PLOG(FATAL) << "failed to fuse_setup";
- }
- } else {
- /* Physical storage is readable by all users on device, but
- * the Android directories are masked off to a single user
- * deep inside attr_from_stat(). */
- if (fuse_setup(&fuse_default, AID_SDCARD_RW, 0006)
- || fuse_setup(&fuse_read, AID_EVERYBODY, full_write ? 0027 : 0022)
- || fuse_setup(&fuse_write, AID_EVERYBODY, full_write ? 0007 : 0022)) {
- PLOG(FATAL) << "failed to fuse_setup";
- }
- }
-
- // Will abort if priv-dropping fails.
- drop_privs(uid, gid);
-
- if (multi_user) {
- fs_prepare_dir(global.obb_path, 0775, uid, gid);
- }
-
- if (pthread_create(&thread_default, NULL, start_handler, &handler_default)
- || pthread_create(&thread_read, NULL, start_handler, &handler_read)
- || pthread_create(&thread_write, NULL, start_handler, &handler_write)) {
- LOG(FATAL) << "failed to pthread_create";
- }
-
- watch_package_list(&global);
- LOG(FATAL) << "terminated prematurely";
-}
-
static bool sdcardfs_setup(const std::string& source_path, const std::string& dest_path,
uid_t fsuid, gid_t fsgid, bool multi_user, userid_t userid, gid_t gid,
- mode_t mask, bool derive_gid) {
- std::string opts = android::base::StringPrintf(
- "fsuid=%d,fsgid=%d,%s%smask=%d,userid=%d,gid=%d", fsuid, fsgid,
- multi_user ? "multiuser," : "", derive_gid ? "derive_gid," : "", mask, userid, gid);
+ mode_t mask, bool derive_gid, bool default_normal) {
+ // Try several attempts, each time with one less option, to gracefully
+ // handle older kernels that aren't updated yet.
+ for (int i = 0; i < 4; i++) {
+ std::string new_opts;
+ if (multi_user && i < 3) new_opts += "multiuser,";
+ if (derive_gid && i < 2) new_opts += "derive_gid,";
+ if (default_normal && i < 1) new_opts += "default_normal,";
- if (mount(source_path.c_str(), dest_path.c_str(), "sdcardfs",
- MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_NOATIME, opts.c_str()) == -1) {
- if (derive_gid) {
- PLOG(ERROR) << "trying to mount sdcardfs filesystem without derive_gid";
- /* Maybe this isn't supported on this kernel. Try without. */
- opts = android::base::StringPrintf("fsuid=%d,fsgid=%d,%smask=%d,userid=%d,gid=%d",
- fsuid, fsgid, multi_user ? "multiuser," : "", mask,
- userid, gid);
- if (mount(source_path.c_str(), dest_path.c_str(), "sdcardfs",
- MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_NOATIME, opts.c_str()) == -1) {
- PLOG(ERROR) << "failed to mount sdcardfs filesystem";
- return false;
- }
+ auto opts = android::base::StringPrintf("fsuid=%d,fsgid=%d,%smask=%d,userid=%d,gid=%d",
+ fsuid, fsgid, new_opts.c_str(), mask, userid, gid);
+ if (mount(source_path.c_str(), dest_path.c_str(), "sdcardfs",
+ MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_NOATIME, opts.c_str()) == -1) {
+ PLOG(WARNING) << "Failed to mount sdcardfs with options " << opts;
} else {
- PLOG(ERROR) << "failed to mount sdcardfs filesystem";
- return false;
+ return true;
}
}
- return true;
+
+ return false;
}
static bool sdcardfs_setup_bind_remount(const std::string& source_path, const std::string& dest_path,
@@ -370,7 +106,7 @@
static void run_sdcardfs(const std::string& source_path, const std::string& label, uid_t uid,
gid_t gid, userid_t userid, bool multi_user, bool full_write,
- bool derive_gid) {
+ bool derive_gid, bool default_normal) {
std::string dest_path_default = "/mnt/runtime/default/" + label;
std::string dest_path_read = "/mnt/runtime/read/" + label;
std::string dest_path_write = "/mnt/runtime/write/" + label;
@@ -380,7 +116,7 @@
// Multi-user storage is fully isolated per user, so "other"
// permissions are completely masked off.
if (!sdcardfs_setup(source_path, dest_path_default, uid, gid, multi_user, userid,
- AID_SDCARD_RW, 0006, derive_gid) ||
+ AID_SDCARD_RW, 0006, derive_gid, default_normal) ||
!sdcardfs_setup_bind_remount(dest_path_default, dest_path_read, AID_EVERYBODY, 0027) ||
!sdcardfs_setup_bind_remount(dest_path_default, dest_path_write, AID_EVERYBODY,
full_write ? 0007 : 0027)) {
@@ -391,7 +127,7 @@
// the Android directories are masked off to a single user
// deep inside attr_from_stat().
if (!sdcardfs_setup(source_path, dest_path_default, uid, gid, multi_user, userid,
- AID_SDCARD_RW, 0006, derive_gid) ||
+ AID_SDCARD_RW, 0006, derive_gid, default_normal) ||
!sdcardfs_setup_bind_remount(dest_path_default, dest_path_read, AID_EVERYBODY,
full_write ? 0027 : 0022) ||
!sdcardfs_setup_bind_remount(dest_path_default, dest_path_write, AID_EVERYBODY,
@@ -411,41 +147,6 @@
exit(0);
}
-static bool supports_sdcardfs(void) {
- std::string filesystems;
- if (!android::base::ReadFileToString("/proc/filesystems", &filesystems)) {
- PLOG(ERROR) << "Could not read /proc/filesystems";
- return false;
- }
- for (const auto& fs : android::base::Split(filesystems, "\n")) {
- if (fs.find("sdcardfs") != std::string::npos) return true;
- }
- return false;
-}
-
-static bool should_use_sdcardfs(void) {
- char property[PROPERTY_VALUE_MAX];
-
- // Allow user to have a strong opinion about state
- property_get(PROP_SDCARDFS_USER, property, "");
- if (!strcmp(property, "force_on")) {
- LOG(WARNING) << "User explicitly enabled sdcardfs";
- return supports_sdcardfs();
- } else if (!strcmp(property, "force_off")) {
- LOG(WARNING) << "User explicitly disabled sdcardfs";
- return false;
- }
-
- // Fall back to device opinion about state
- if (property_get_bool(PROP_SDCARDFS_DEVICE, true)) {
- LOG(WARNING) << "Device explicitly enabled sdcardfs";
- return supports_sdcardfs();
- } else {
- LOG(WARNING) << "Device explicitly disabled sdcardfs";
- return false;
- }
-}
-
static int usage() {
LOG(ERROR) << "usage: sdcard [OPTIONS] <source_path> <label>"
<< " -u: specify UID to run as"
@@ -466,12 +167,16 @@
bool multi_user = false;
bool full_write = false;
bool derive_gid = false;
+ bool default_normal = false;
int i;
struct rlimit rlim;
int fs_version;
+ setenv("ANDROID_LOG_TAGS", "*:v", 1);
+ android::base::InitLogging(argv, android::base::LogdLogger(android::base::SYSTEM));
+
int opt;
- while ((opt = getopt(argc, argv, "u:g:U:mwG")) != -1) {
+ while ((opt = getopt(argc, argv, "u:g:U:mwGi")) != -1) {
switch (opt) {
case 'u':
uid = strtoul(optarg, NULL, 10);
@@ -491,6 +196,9 @@
case 'G':
derive_gid = true;
break;
+ case 'i':
+ default_normal = true;
+ break;
case '?':
default:
return usage();
@@ -533,10 +241,7 @@
sleep(1);
}
- if (should_use_sdcardfs()) {
- run_sdcardfs(source_path, label, uid, gid, userid, multi_user, full_write, derive_gid);
- } else {
- run(source_path, label, uid, gid, userid, multi_user, full_write);
- }
+ run_sdcardfs(source_path, label, uid, gid, userid, multi_user, full_write, derive_gid,
+ default_normal);
return 1;
}
diff --git a/shell_and_utilities/README.md b/shell_and_utilities/README.md
index 206204b..c423c69 100644
--- a/shell_and_utilities/README.md
+++ b/shell_and_utilities/README.md
@@ -175,18 +175,18 @@
one-true-awk: awk
-toolbox: getevent newfs\_msdos
+toolbox: getevent getprop newfs\_msdos
toybox: acpi base64 basename blockdev cal cat chcon chgrp chmod chown
chroot chrt cksum clear cmp comm cp cpio cut date df diff dirname dmesg
dos2unix du echo env expand expr fallocate false file find flock free
-getenforce getprop groups gunzip gzip head hostname hwclock id ifconfig
-inotifyd insmod ionice iorenice kill killall ln load\_policy log logname
-losetup ls lsmod lsof lspci lsusb md5sum microcom mkdir mkfifo mknod
-mkswap mktemp modinfo modprobe more mount mountpoint mv netstat nice
-nl nohup od paste patch pgrep pidof pkill pmap printenv printf ps pwd
-readlink realpath renice restorecon rm rmdir rmmod runcon sed sendevent
-seq setenforce setprop setsid sha1sum sha224sum sha256sum sha384sum
+getenforce groups gunzip gzip head hostname hwclock id ifconfig inotifyd
+insmod ionice iorenice kill killall ln load\_policy log logname losetup
+ls lsmod lsof lspci lsusb md5sum microcom mkdir mkfifo mknod mkswap
+mktemp modinfo modprobe more mount mountpoint mv netstat nice nl nohup
+od paste patch pgrep pidof pkill pmap printenv printf ps pwd readlink
+realpath renice restorecon rm rmdir rmmod runcon sed sendevent seq
+setenforce setprop setsid sha1sum sha224sum sha256sum sha384sum
sha512sum sleep sort split start stat stop strings swapoff swapon sync
sysctl tac tail tar taskset tee time timeout top touch tr true truncate
tty ulimit umount uname uniq unix2dos uptime usleep uudecode uuencode
diff --git a/storaged/Android.bp b/storaged/Android.bp
index 2c7dea1..b478f4a 100644
--- a/storaged/Android.bp
+++ b/storaged/Android.bp
@@ -65,6 +65,8 @@
"binder/android/os/storaged/IStoragedPrivate.aidl",
],
+ static_libs: ["libhealthhalutils"],
+
logtags: ["EventLogTags.logtags"],
proto: {
@@ -84,7 +86,10 @@
srcs: ["main.cpp"],
- static_libs: ["libstoraged"],
+ static_libs: [
+ "libhealthhalutils",
+ "libstoraged",
+ ],
}
/*
@@ -98,7 +103,10 @@
srcs: ["tests/storaged_test.cpp"],
- static_libs: ["libstoraged"],
+ static_libs: [
+ "libhealthhalutils",
+ "libstoraged",
+ ],
}
// AIDL interface between storaged and framework.jar
diff --git a/storaged/binder/android/os/IStoraged.aidl b/storaged/binder/android/os/IStoraged.aidl
index f81e904..0bcc70c 100644
--- a/storaged/binder/android/os/IStoraged.aidl
+++ b/storaged/binder/android/os/IStoraged.aidl
@@ -20,4 +20,5 @@
interface IStoraged {
void onUserStarted(int userId);
void onUserStopped(int userId);
+ int getRecentPerf();
}
\ No newline at end of file
diff --git a/storaged/include/storaged.h b/storaged/include/storaged.h
index 6bf6c9c..400e734 100644
--- a/storaged/include/storaged.h
+++ b/storaged/include/storaged.h
@@ -81,7 +81,7 @@
private:
time_t mTimer;
storaged_config mConfig;
- disk_stats_monitor mDsm;
+ unique_ptr<disk_stats_monitor> mDsm;
uid_monitor mUidm;
time_t mStarttime;
sp<android::hardware::health::V2_0::IHealth> health;
@@ -96,8 +96,11 @@
return string("/data/misc_ce/") + to_string(user_id) +
"/storaged/storaged.proto";
}
-public:
+ void init_health_service();
+
+ public:
storaged_t(void);
+ void init(void);
void event(void);
void event_checked(void);
void pause(void) {
@@ -116,6 +119,8 @@
return storage_info->get_perf_history();
}
+ uint32_t get_recent_perf(void) { return storage_info->get_recent_perf(); }
+
map<uint64_t, struct uid_records> get_uid_records(
double hours, uint64_t threshold, bool force_report) {
return mUidm.dump(hours, threshold, force_report);
@@ -130,9 +135,8 @@
void add_user_ce(userid_t user_id);
void remove_user_ce(userid_t user_id);
- void init_health_service();
virtual ::android::hardware::Return<void> healthInfoChanged(
- const ::android::hardware::health::V1_0::HealthInfo& info);
+ const ::android::hardware::health::V2_0::HealthInfo& info);
void serviceDied(uint64_t cookie, const wp<::android::hidl::base::V1_0::IBase>& who);
void report_storage_info();
diff --git a/storaged/include/storaged_diskstats.h b/storaged/include/storaged_diskstats.h
index ff030f6..0b93ba6 100644
--- a/storaged/include/storaged_diskstats.h
+++ b/storaged/include/storaged_diskstats.h
@@ -19,6 +19,8 @@
#include <stdint.h>
+#include <android/hardware/health/2.0/IHealth.h>
+
// number of attributes diskstats has
#define DISK_STATS_SIZE ( 11 )
@@ -160,6 +162,7 @@
const double mSigma;
struct disk_perf mMean;
struct disk_perf mStd;
+ android::sp<android::hardware::health::V2_0::IHealth> mHealth;
void update_mean();
void update_std();
@@ -170,21 +173,27 @@
void update(struct disk_stats* stats);
public:
- disk_stats_monitor(uint32_t window_size = 5, double sigma = 1.0) :
- DISK_STATS_PATH(access(MMC_DISK_STATS_PATH, R_OK) ?
- (access(SDA_DISK_STATS_PATH, R_OK) ?
- nullptr :
- SDA_DISK_STATS_PATH) :
- MMC_DISK_STATS_PATH),
- mPrevious(), mAccumulate(), mAccumulate_pub(),
- mStall(false), mValid(false),
- mWindow(window_size), mSigma(sigma),
- mMean(), mStd() {}
- bool enabled() {
- return DISK_STATS_PATH != nullptr;
- }
- void update(void);
- void publish(void);
+ disk_stats_monitor(const android::sp<android::hardware::health::V2_0::IHealth>& healthService,
+ uint32_t window_size = 5, double sigma = 1.0)
+ : DISK_STATS_PATH(
+ healthService != nullptr
+ ? nullptr
+ : (access(MMC_DISK_STATS_PATH, R_OK) == 0
+ ? MMC_DISK_STATS_PATH
+ : (access(SDA_DISK_STATS_PATH, R_OK) == 0 ? SDA_DISK_STATS_PATH : nullptr))),
+ mPrevious(),
+ mAccumulate(),
+ mAccumulate_pub(),
+ mStall(false),
+ mValid(false),
+ mWindow(window_size),
+ mSigma(sigma),
+ mMean(),
+ mStd(),
+ mHealth(healthService) {}
+ bool enabled() { return mHealth != nullptr || DISK_STATS_PATH != nullptr; }
+ void update(void);
+ void publish(void);
};
-#endif /* _STORAGED_DISKSTATS_H_ */
\ No newline at end of file
+#endif /* _STORAGED_DISKSTATS_H_ */
diff --git a/storaged/include/storaged_info.h b/storaged/include/storaged_info.h
index b1efac2..88a53de 100644
--- a/storaged/include/storaged_info.h
+++ b/storaged/include/storaged_info.h
@@ -21,6 +21,7 @@
#include <chrono>
+#include <android/hardware/health/2.0/IHealth.h>
#include <utils/Mutex.h>
#include "storaged.h"
@@ -35,7 +36,7 @@
using namespace storaged_proto;
class storage_info_t {
-protected:
+ protected:
FRIEND_TEST(storaged_test, storage_info_t);
// emmc lifetime
uint16_t eol; // pre-eol (end of life) information
@@ -66,8 +67,10 @@
}
void publish();
storage_info_t* s_info;
-public:
- static storage_info_t* get_storage_info();
+
+ public:
+ static storage_info_t* get_storage_info(
+ const sp<android::hardware::health::V2_0::IHealth>& healthService);
virtual ~storage_info_t() {};
virtual void report() {};
void load_perf_history_proto(const IOPerfHistory& perf_history);
@@ -75,6 +78,7 @@
void update_perf_history(uint32_t bw,
const time_point<system_clock>& tp);
vector<int> get_perf_history();
+ uint32_t get_recent_perf();
};
class emmc_info_t : public storage_info_t {
@@ -98,4 +102,18 @@
virtual void report();
};
+class health_storage_info_t : public storage_info_t {
+ private:
+ using IHealth = hardware::health::V2_0::IHealth;
+ using StorageInfo = hardware::health::V2_0::StorageInfo;
+
+ sp<IHealth> mHealth;
+ void set_values_from_hal_storage_info(const StorageInfo& halInfo);
+
+ public:
+ health_storage_info_t(const sp<IHealth>& service) : mHealth(service){};
+ virtual ~health_storage_info_t() {}
+ virtual void report();
+};
+
#endif /* _STORAGED_INFO_H_ */
diff --git a/storaged/include/storaged_service.h b/storaged/include/storaged_service.h
index 05c3b94..7ec6864 100644
--- a/storaged/include/storaged_service.h
+++ b/storaged/include/storaged_service.h
@@ -39,6 +39,7 @@
binder::Status onUserStarted(int32_t userId);
binder::Status onUserStopped(int32_t userId);
+ binder::Status getRecentPerf(int32_t* _aidl_return);
};
class StoragedPrivateService : public BinderService<StoragedPrivateService>, public BnStoragedPrivate {
diff --git a/storaged/main.cpp b/storaged/main.cpp
index c1b1329..b3f1281 100644
--- a/storaged/main.cpp
+++ b/storaged/main.cpp
@@ -51,7 +51,7 @@
void* storaged_main(void* /* unused */) {
storaged_sp = new storaged_t();
- storaged_sp->init_health_service();
+ storaged_sp->init();
storaged_sp->report_storage_info();
LOG_TO(SYSTEM, INFO) << "storaged: Start";
diff --git a/storaged/storaged.cpp b/storaged/storaged.cpp
index ff0de29..bf8b448 100644
--- a/storaged/storaged.cpp
+++ b/storaged/storaged.cpp
@@ -28,11 +28,12 @@
#include <sstream>
#include <string>
-#include <android/hidl/manager/1.0/IServiceManager.h>
#include <android-base/file.h>
#include <android-base/logging.h>
+#include <android/hidl/manager/1.0/IServiceManager.h>
#include <batteryservice/BatteryServiceConstants.h>
#include <cutils/properties.h>
+#include <healthhalutils/HealthHalUtils.h>
#include <hidl/HidlTransportSupport.h>
#include <hwbinder/IPCThreadState.h>
#include <log/log.h>
@@ -62,25 +63,16 @@
const uint32_t storaged_t::current_version = 4;
-using android::hardware::health::V1_0::BatteryStatus;
-using android::hardware::health::V1_0::toString;
-using android::hardware::health::V1_0::HealthInfo;
-using android::hardware::health::V2_0::IHealth;
-using android::hardware::health::V2_0::Result;
using android::hardware::interfacesEqual;
using android::hardware::Return;
+using android::hardware::health::V1_0::BatteryStatus;
+using android::hardware::health::V1_0::toString;
+using android::hardware::health::V2_0::get_health_service;
+using android::hardware::health::V2_0::HealthInfo;
+using android::hardware::health::V2_0::IHealth;
+using android::hardware::health::V2_0::Result;
using android::hidl::manager::V1_0::IServiceManager;
-static sp<IHealth> get_health_service() {
- for (auto&& instanceName : {"default", "backup"}) {
- auto ret = IHealth::getService(instanceName);
- if (ret != nullptr) {
- return ret;
- }
- LOG_TO(SYSTEM, INFO) << "health: storaged: cannot get " << instanceName << " service";
- }
- return nullptr;
-}
inline charger_stat_t is_charger_on(BatteryStatus prop) {
return (prop == BatteryStatus::CHARGING || prop == BatteryStatus::FULL) ?
@@ -88,10 +80,16 @@
}
Return<void> storaged_t::healthInfoChanged(const HealthInfo& props) {
- mUidm.set_charger_state(is_charger_on(props.batteryStatus));
+ mUidm.set_charger_state(is_charger_on(props.legacy.batteryStatus));
return android::hardware::Void();
}
+void storaged_t::init() {
+ init_health_service();
+ mDsm = std::make_unique<disk_stats_monitor>(health);
+ storage_info.reset(storage_info_t::get_storage_info(health));
+}
+
void storaged_t::init_health_service() {
if (!mUidm.enabled())
return;
@@ -160,8 +158,6 @@
property_get_int32("ro.storaged.flush_proto.interval",
DEFAULT_PERIODIC_CHORES_INTERVAL_FLUSH_PROTO);
- storage_info.reset(storage_info_t::get_storage_info());
-
mStarttime = time(NULL);
mTimer = 0;
}
@@ -315,10 +311,10 @@
void storaged_t::event(void) {
unordered_map<int, StoragedProto> protos;
- if (mDsm.enabled()) {
- mDsm.update();
+ if (mDsm->enabled()) {
+ mDsm->update();
if (!(mTimer % mConfig.periodic_chores_interval_disk_stats_publish)) {
- mDsm.publish();
+ mDsm->publish();
}
}
diff --git a/storaged/storaged_diskstats.cpp b/storaged/storaged_diskstats.cpp
index 0604e0a..1050033 100644
--- a/storaged/storaged_diskstats.cpp
+++ b/storaged/storaged_diskstats.cpp
@@ -30,6 +30,12 @@
namespace {
+using android::sp;
+using android::hardware::health::V2_0::DiskStats;
+using android::hardware::health::V2_0::IHealth;
+using android::hardware::health::V2_0::Result;
+using android::hardware::health::V2_0::toString;
+
#ifdef DEBUG
void log_debug_disk_perf(struct disk_perf* perf, const char* type) {
// skip if the input structure are all zeros
@@ -60,17 +66,30 @@
} // namespace
-bool parse_disk_stats(const char* disk_stats_path, struct disk_stats* stats)
-{
- // Get time
- struct timespec ts;
+bool get_time(struct timespec* ts) {
// Use monotonic to exclude suspend time so that we measure IO bytes/sec
// when system is running.
- int ret = clock_gettime(CLOCK_MONOTONIC, &ts);
+ int ret = clock_gettime(CLOCK_MONOTONIC, ts);
if (ret < 0) {
PLOG_TO(SYSTEM, ERROR) << "clock_gettime() failed";
return false;
}
+ return true;
+}
+
+void init_disk_stats_other(const struct timespec& ts, struct disk_stats* stats) {
+ stats->start_time = 0;
+ stats->end_time = (uint64_t)ts.tv_sec * SEC_TO_MSEC + ts.tv_nsec / (MSEC_TO_USEC * USEC_TO_NSEC);
+ stats->counter = 1;
+ stats->io_avg = (double)stats->io_in_flight;
+}
+
+bool parse_disk_stats(const char* disk_stats_path, struct disk_stats* stats) {
+ // Get time
+ struct timespec ts;
+ if (!get_time(&ts)) {
+ return false;
+ }
std::string buffer;
if (!android::base::ReadFileToString(disk_stats_path, &buffer)) {
@@ -84,11 +103,52 @@
ss >> *((uint64_t*)stats + i);
}
// Other entries
- stats->start_time = 0;
- stats->end_time = (uint64_t)ts.tv_sec * SEC_TO_MSEC +
- ts.tv_nsec / (MSEC_TO_USEC * USEC_TO_NSEC);
- stats->counter = 1;
- stats->io_avg = (double)stats->io_in_flight;
+ init_disk_stats_other(ts, stats);
+ return true;
+}
+
+void convert_hal_disk_stats(struct disk_stats* dst, const DiskStats& src) {
+ dst->read_ios = src.reads;
+ dst->read_merges = src.readMerges;
+ dst->read_sectors = src.readSectors;
+ dst->read_ticks = src.readTicks;
+ dst->write_ios = src.writes;
+ dst->write_merges = src.writeMerges;
+ dst->write_sectors = src.writeSectors;
+ dst->write_ticks = src.writeTicks;
+ dst->io_in_flight = src.ioInFlight;
+ dst->io_ticks = src.ioTicks;
+ dst->io_in_queue = src.ioInQueue;
+}
+
+bool get_disk_stats_from_health_hal(const sp<IHealth>& service, struct disk_stats* stats) {
+ struct timespec ts;
+ if (!get_time(&ts)) {
+ return false;
+ }
+
+ bool success = false;
+ auto ret = service->getDiskStats([&success, stats](auto result, const auto& halStats) {
+ if (result != Result::SUCCESS || halStats.size() == 0) {
+ LOG_TO(SYSTEM, ERROR) << "getDiskStats failed with result " << toString(result)
+ << " and size " << halStats.size();
+ return;
+ }
+
+ convert_hal_disk_stats(stats, halStats[0]);
+ success = true;
+ });
+
+ if (!ret.isOk()) {
+ LOG_TO(SYSTEM, ERROR) << "getDiskStats failed with " << ret.description();
+ return false;
+ }
+
+ if (!success) {
+ return false;
+ }
+
+ init_disk_stats_other(ts, stats);
return true;
}
@@ -243,8 +303,14 @@
void disk_stats_monitor::update() {
disk_stats curr;
- if (!parse_disk_stats(DISK_STATS_PATH, &curr)) {
- return;
+ if (mHealth != nullptr) {
+ if (!get_disk_stats_from_health_hal(mHealth, &curr)) {
+ return;
+ }
+ } else {
+ if (!parse_disk_stats(DISK_STATS_PATH, &curr)) {
+ return;
+ }
}
update(&curr);
diff --git a/storaged/storaged_info.cpp b/storaged/storaged_info.cpp
index 036d7e1..055f375 100644
--- a/storaged/storaged_info.cpp
+++ b/storaged/storaged_info.cpp
@@ -36,6 +36,10 @@
using namespace android::base;
using namespace storaged_proto;
+using android::hardware::health::V2_0::IHealth;
+using android::hardware::health::V2_0::Result;
+using android::hardware::health::V2_0::StorageInfo;
+
const string emmc_info_t::emmc_sysfs = "/sys/bus/mmc/devices/mmc0:0001/";
const string emmc_info_t::emmc_debugfs = "/d/mmc0/mmc0:0001/ext_csd";
const char* emmc_info_t::emmc_ver_str[9] = {
@@ -54,8 +58,10 @@
} // namespace
-storage_info_t* storage_info_t::get_storage_info()
-{
+storage_info_t* storage_info_t::get_storage_info(const sp<IHealth>& healthService) {
+ if (healthService != nullptr) {
+ return new health_storage_info_t(healthService);
+ }
if (FileExists(emmc_info_t::emmc_sysfs) ||
FileExists(emmc_info_t::emmc_debugfs)) {
return new emmc_info_t;
@@ -213,6 +219,13 @@
return ret;
}
+uint32_t storage_info_t::get_recent_perf() {
+ Mutex::Autolock _l(si_mutex);
+ if (recent_perf.size() == 0) return 0;
+ return accumulate(recent_perf.begin(), recent_perf.end(), recent_perf.size() / 2) /
+ recent_perf.size();
+}
+
void emmc_info_t::report()
{
if (!report_sysfs() && !report_debugfs())
@@ -351,3 +364,25 @@
publish();
}
+void health_storage_info_t::report() {
+ auto ret = mHealth->getStorageInfo([this](auto result, const auto& halInfos) {
+ if (result != Result::SUCCESS || halInfos.size() == 0) {
+ LOG_TO(SYSTEM, DEBUG) << "getStorageInfo failed with result " << toString(result)
+ << " and size " << halInfos.size();
+ return;
+ }
+ set_values_from_hal_storage_info(halInfos[0]);
+ publish();
+ });
+
+ if (!ret.isOk()) {
+ LOG_TO(SYSTEM, DEBUG) << "getStorageInfo failed with " << ret.description();
+ }
+}
+
+void health_storage_info_t::set_values_from_hal_storage_info(const StorageInfo& halInfo) {
+ eol = halInfo.eol;
+ lifetime_a = halInfo.lifetimeA;
+ lifetime_b = halInfo.lifetimeB;
+ version = halInfo.version;
+}
diff --git a/storaged/storaged_service.cpp b/storaged/storaged_service.cpp
index 3c790e6..17ea25b 100644
--- a/storaged/storaged_service.cpp
+++ b/storaged/storaged_service.cpp
@@ -174,6 +174,16 @@
return binder::Status::ok();
}
+binder::Status StoragedService::getRecentPerf(int32_t* _aidl_return) {
+ uint32_t recent_perf = storaged_sp->get_recent_perf();
+ if (recent_perf > INT32_MAX) {
+ *_aidl_return = INT32_MAX;
+ } else {
+ *_aidl_return = static_cast<int32_t>(recent_perf);
+ }
+ return binder::Status::ok();
+}
+
status_t StoragedPrivateService::start() {
return BinderService<StoragedPrivateService>::publish();
}
diff --git a/storaged/tests/storaged_test.cpp b/storaged/tests/storaged_test.cpp
index 6a5fc61..d1fa9ed 100644
--- a/storaged/tests/storaged_test.cpp
+++ b/storaged/tests/storaged_test.cpp
@@ -25,6 +25,7 @@
#include <gtest/gtest.h>
+#include <healthhalutils/HealthHalUtils.h>
#include <storaged.h> // data structures
#include <storaged_utils.h> // functions to test
@@ -234,10 +235,17 @@
}
TEST(storaged_test, disk_stats_monitor) {
+ using android::hardware::health::V2_0::get_health_service;
+
+ auto healthService = get_health_service();
+
// asserting that there is one file for diskstats
- ASSERT_TRUE(access(MMC_DISK_STATS_PATH, R_OK) >= 0 || access(SDA_DISK_STATS_PATH, R_OK) >= 0);
+ ASSERT_TRUE(healthService != nullptr || access(MMC_DISK_STATS_PATH, R_OK) >= 0 ||
+ access(SDA_DISK_STATS_PATH, R_OK) >= 0);
+
// testing if detect() will return the right value
- disk_stats_monitor dsm_detect;
+ disk_stats_monitor dsm_detect{healthService};
+ ASSERT_TRUE(dsm_detect.enabled());
// feed monitor with constant perf data for io perf baseline
// using constant perf is reasonable since the functionality of stream_stats
// has already been tested
@@ -280,7 +288,7 @@
}
// testing if stalled disk_stats can be correctly accumulated in the monitor
- disk_stats_monitor dsm_acc;
+ disk_stats_monitor dsm_acc{healthService};
struct disk_stats norm_inc = {
.read_ios = 200,
.read_merges = 0,
diff --git a/toolbox/Android.bp b/toolbox/Android.bp
index 02365c6..9f7fc3b 100644
--- a/toolbox/Android.bp
+++ b/toolbox/Android.bp
@@ -44,9 +44,11 @@
cc_defaults {
name: "toolbox_binary_defaults",
defaults: ["toolbox_defaults"],
+ cpp_std: "experimental",
srcs: [
"toolbox.c",
"getevent.c",
+ "getprop.cpp",
"newfs_msdos.c",
],
generated_headers: [
@@ -54,12 +56,15 @@
],
whole_static_libs: ["libtoolbox_dd"],
shared_libs: [
+ "libbase",
"libcutils",
],
+ static_libs: ["libpropertyinfoparser"],
symlinks: [
"dd",
"getevent",
+ "getprop",
"newfs_msdos",
],
}
diff --git a/toolbox/getprop.cpp b/toolbox/getprop.cpp
new file mode 100644
index 0000000..9e324a0
--- /dev/null
+++ b/toolbox/getprop.cpp
@@ -0,0 +1,161 @@
+//
+// Copyright (C) 2017 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 <getopt.h>
+#include <sys/system_properties.h>
+
+#include <iostream>
+#include <string>
+#include <vector>
+
+#include <android-base/properties.h>
+#include <property_info_parser/property_info_parser.h>
+
+using android::base::GetProperty;
+using android::properties::PropertyInfoAreaFile;
+
+PropertyInfoAreaFile property_info_file;
+
+enum class ResultType {
+ Value,
+ Context,
+ Type,
+};
+
+void PrintAllProperties(ResultType result_type) {
+ std::vector<std::pair<std::string, std::string>> properties;
+ __system_property_foreach(
+ [](const prop_info* pi, void* cookie) {
+ __system_property_read_callback(
+ pi,
+ [](void* cookie, const char* name, const char* value, unsigned) {
+ auto properties =
+ reinterpret_cast<std::vector<std::pair<std::string, std::string>>*>(cookie);
+ properties->emplace_back(name, value);
+ },
+ cookie);
+ },
+ &properties);
+
+ std::sort(properties.begin(), properties.end());
+
+ if (result_type != ResultType::Value) {
+ for (auto& [name, value] : properties) {
+ const char* context = nullptr;
+ const char* type = nullptr;
+ property_info_file->GetPropertyInfo(name.c_str(), &context, &type);
+ if (result_type == ResultType::Context) {
+ value = context;
+ } else {
+ value = type;
+ }
+ }
+ }
+
+ for (const auto& [name, value] : properties) {
+ std::cout << "[" << name << "]: [" << value << "]" << std::endl;
+ }
+}
+
+void PrintProperty(const char* name, const char* default_value, ResultType result_type) {
+ switch (result_type) {
+ case ResultType::Value:
+ std::cout << GetProperty(name, default_value) << std::endl;
+ break;
+ case ResultType::Context: {
+ const char* context = nullptr;
+ property_info_file->GetPropertyInfo(name, &context, nullptr);
+ std::cout << context << std::endl;
+ break;
+ }
+ case ResultType::Type: {
+ const char* type = nullptr;
+ property_info_file->GetPropertyInfo(name, nullptr, &type);
+ std::cout << type << std::endl;
+ break;
+ }
+ }
+}
+
+extern "C" int getprop_main(int argc, char** argv) {
+ auto result_type = ResultType::Value;
+
+ while (true) {
+ static const struct option long_options[] = {
+ {"help", no_argument, nullptr, 'h'},
+ {nullptr, 0, nullptr, 0},
+ };
+
+ int arg = getopt_long(argc, argv, "TZ", long_options, nullptr);
+
+ if (arg == -1) {
+ break;
+ }
+
+ switch (arg) {
+ case 'h':
+ std::cout << "usage: getprop [-TZ] [NAME [DEFAULT]]\n"
+ "\n"
+ "Gets an Android system property, or lists them all.\n"
+ "\n"
+ "-T\tShow property types instead of values\n"
+ "-Z\tShow property contexts instead of values\n"
+ << std::endl;
+ return 0;
+ case 'T':
+ if (result_type != ResultType::Value) {
+ std::cerr << "Only one of -T or -Z may be specified" << std::endl;
+ return -1;
+ }
+ result_type = ResultType::Type;
+ break;
+ case 'Z':
+ if (result_type != ResultType::Value) {
+ std::cerr << "Only one of -T or -Z may be specified" << std::endl;
+ return -1;
+ }
+ result_type = ResultType::Context;
+ break;
+ case '?':
+ return -1;
+ default:
+ std::cerr << "getprop: getopt returned invalid result: " << arg << std::endl;
+ return -1;
+ }
+ }
+
+ if (result_type != ResultType::Value) {
+ property_info_file.LoadDefaultPath();
+ if (!property_info_file) {
+ std::cerr << "Unable to load property info file" << std::endl;
+ return -1;
+ }
+ }
+
+ if (optind >= argc) {
+ PrintAllProperties(result_type);
+ return 0;
+ }
+
+ if (optind < argc - 2) {
+ std::cerr << "getprop: Max 2 arguments (see \"getprop --help\")" << std::endl;
+ return -1;
+ }
+
+ PrintProperty(argv[optind], (optind == argc - 1) ? "" : argv[optind + 1], result_type);
+
+ return 0;
+}
diff --git a/toolbox/tools.h b/toolbox/tools.h
index 9134af3..505f528 100644
--- a/toolbox/tools.h
+++ b/toolbox/tools.h
@@ -1,4 +1,5 @@
TOOL(dd)
TOOL(getevent)
+TOOL(getprop)
TOOL(newfs_msdos)
TOOL(toolbox)