Merge "ZipWriter: Do not write DataDescriptor for STORED files"
diff --git a/.clang-format-2 b/.clang-format-2
index aab4665..41591ce 100644
--- a/.clang-format-2
+++ b/.clang-format-2
@@ -1,4 +1,5 @@
BasedOnStyle: Google
+AllowShortFunctionsOnASingleLine: Inline
ColumnLimit: 100
CommentPragmas: NOLINT:.*
DerivePointerAlignment: false
diff --git a/.clang-format-4 b/.clang-format-4
index 1497447..ae4a451 100644
--- a/.clang-format-4
+++ b/.clang-format-4
@@ -1,5 +1,6 @@
BasedOnStyle: Google
AccessModifierOffset: -2
+AllowShortFunctionsOnASingleLine: Inline
ColumnLimit: 100
CommentPragmas: NOLINT:.*
DerivePointerAlignment: false
diff --git a/adb/Android.mk b/adb/Android.mk
index 8a43e37..e841205 100644
--- a/adb/Android.mk
+++ b/adb/Android.mk
@@ -124,12 +124,11 @@
adbd_auth.cpp \
jdwp_service.cpp \
-LOCAL_C_INCLUDES := system/core/qemu_pipe/include
LOCAL_SANITIZE := $(adb_target_sanitize)
# Even though we're building a static library (and thus there's no link step for
# this to take effect), this adds the includes to our path.
-LOCAL_STATIC_LIBRARIES := libcrypto_utils libcrypto libqemu_pipe libbase
+LOCAL_STATIC_LIBRARIES := libcrypto_utils libcrypto libbase
LOCAL_WHOLE_STATIC_LIBRARIES := libadbd_usb
@@ -362,7 +361,6 @@
LOCAL_STATIC_LIBRARIES := \
libadbd \
libbase \
- libqemu_pipe \
libbootloader_message \
libfs_mgr \
libfec \
diff --git a/adb/adb.cpp b/adb/adb.cpp
index 577e9b9..cf6b359 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -59,10 +59,12 @@
std::string adb_version() {
// Don't change the format of this --- it's parsed by ddmlib.
- return android::base::StringPrintf("Android Debug Bridge version %d.%d.%d\n"
- "Revision %s\n",
- ADB_VERSION_MAJOR, ADB_VERSION_MINOR, ADB_SERVER_VERSION,
- ADB_REVISION);
+ return android::base::StringPrintf(
+ "Android Debug Bridge version %d.%d.%d\n"
+ "Revision %s\n"
+ "Installed as %s\n",
+ ADB_VERSION_MAJOR, ADB_VERSION_MINOR, ADB_SERVER_VERSION, ADB_REVISION,
+ android::base::GetExecutablePath().c_str());
}
void fatal(const char *fmt, ...) {
diff --git a/adb/adb_utils.cpp b/adb/adb_utils.cpp
index 7058acb..31d3dc6 100644
--- a/adb/adb_utils.cpp
+++ b/adb/adb_utils.cpp
@@ -75,7 +75,7 @@
bool directory_exists(const std::string& path) {
struct stat sb;
- return lstat(path.c_str(), &sb) != -1 && S_ISDIR(sb.st_mode);
+ return stat(path.c_str(), &sb) != -1 && S_ISDIR(sb.st_mode);
}
std::string escape_arg(const std::string& s) {
diff --git a/adb/adb_utils_test.cpp b/adb/adb_utils_test.cpp
index a3bc445..e1b6287 100644
--- a/adb/adb_utils_test.cpp
+++ b/adb/adb_utils_test.cpp
@@ -55,7 +55,6 @@
ASSERT_FALSE(directory_exists(subdir(profiles_dir, "does-not-exist")));
#else
ASSERT_TRUE(directory_exists("/proc"));
- ASSERT_FALSE(directory_exists("/proc/self")); // Symbolic link.
ASSERT_FALSE(directory_exists("/proc/does-not-exist"));
#endif
}
diff --git a/adb/client/usb_libusb.cpp b/adb/client/usb_libusb.cpp
index 7adb262..c48a251 100644
--- a/adb/client/usb_libusb.cpp
+++ b/adb/client/usb_libusb.cpp
@@ -306,14 +306,6 @@
}
device_serial.resize(rc);
- // Try to reset the device.
- rc = libusb_reset_device(handle_raw);
- if (rc != 0) {
- LOG(WARNING) << "failed to reset opened device '" << device_serial
- << "': " << libusb_error_name(rc);
- continue;
- }
-
// WARNING: this isn't released via RAII.
rc = libusb_claim_interface(handle.get(), interface_num);
if (rc != 0) {
diff --git a/adb/commandline.cpp b/adb/commandline.cpp
index 3de7be6..4979eef 100644
--- a/adb/commandline.cpp
+++ b/adb/commandline.cpp
@@ -782,9 +782,16 @@
return RemoteShell(use_shell_protocol, shell_type_arg, escape_char, command);
}
-static int adb_download_buffer(const char *service, const char *fn, const void* data, unsigned sz,
- bool show_progress)
-{
+static int adb_download_buffer(const char* service, const char* filename) {
+ std::string content;
+ if (!android::base::ReadFileToString(filename, &content)) {
+ fprintf(stderr, "error: couldn't read %s: %s\n", filename, strerror(errno));
+ return -1;
+ }
+
+ const uint8_t* data = reinterpret_cast<const uint8_t*>(content.data());
+ unsigned sz = content.size();
+
std::string error;
int fd = adb_connect(android::base::StringPrintf("%s:%d", service, sz), &error);
if (fd < 0) {
@@ -798,10 +805,8 @@
unsigned total = sz;
const uint8_t* ptr = reinterpret_cast<const uint8_t*>(data);
- if (show_progress) {
- const char* x = strrchr(service, ':');
- if (x) service = x + 1;
- }
+ const char* x = strrchr(service, ':');
+ if (x) service = x + 1;
while (sz > 0) {
unsigned xfer = (sz > CHUNK_SIZE) ? CHUNK_SIZE : sz;
@@ -814,14 +819,10 @@
}
sz -= xfer;
ptr += xfer;
- if (show_progress) {
- printf("sending: '%s' %4d%% \r", fn, (int)(100LL - ((100LL * sz) / (total))));
- fflush(stdout);
- }
+ printf("sending: '%s' %4d%% \r", filename, (int)(100LL - ((100LL * sz) / (total))));
+ fflush(stdout);
}
- if (show_progress) {
- printf("\n");
- }
+ printf("\n");
if (!adb_status(fd, &error)) {
fprintf(stderr,"* error response '%s' *\n", error.c_str());
@@ -854,38 +855,40 @@
* - When the other side sends "DONEDONE" instead of a block number,
* we hang up.
*/
-static int adb_sideload_host(const char* fn) {
- fprintf(stderr, "loading: '%s'...\n", fn);
-
- std::string content;
- if (!android::base::ReadFileToString(fn, &content)) {
- fprintf(stderr, "failed: %s\n", strerror(errno));
+static int adb_sideload_host(const char* filename) {
+ fprintf(stderr, "opening '%s'...\n", filename);
+ struct stat sb;
+ if (stat(filename, &sb) == -1) {
+ fprintf(stderr, "failed to stat file %s: %s\n", filename, strerror(errno));
+ return -1;
+ }
+ unique_fd package_fd(adb_open(filename, O_RDONLY));
+ if (package_fd == -1) {
+ fprintf(stderr, "failed to open file %s: %s\n", filename, strerror(errno));
return -1;
}
- const uint8_t* data = reinterpret_cast<const uint8_t*>(content.data());
- unsigned sz = content.size();
-
fprintf(stderr, "connecting...\n");
- std::string service =
- android::base::StringPrintf("sideload-host:%d:%d", sz, SIDELOAD_HOST_BLOCK_SIZE);
+ std::string service = android::base::StringPrintf(
+ "sideload-host:%d:%d", static_cast<int>(sb.st_size), SIDELOAD_HOST_BLOCK_SIZE);
std::string error;
- unique_fd fd(adb_connect(service, &error));
- if (fd < 0) {
- // Try falling back to the older sideload method. Maybe this
+ unique_fd device_fd(adb_connect(service, &error));
+ if (device_fd < 0) {
+ // Try falling back to the older (<= K) sideload method. Maybe this
// is an older device that doesn't support sideload-host.
fprintf(stderr, "falling back to older sideload method...\n");
- return adb_download_buffer("sideload", fn, data, sz, true);
+ return adb_download_buffer("sideload", filename);
}
int opt = SIDELOAD_HOST_BLOCK_SIZE;
- adb_setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &opt, sizeof(opt));
+ adb_setsockopt(device_fd, SOL_SOCKET, SO_SNDBUF, &opt, sizeof(opt));
+
+ char buf[SIDELOAD_HOST_BLOCK_SIZE];
size_t xfer = 0;
int last_percent = -1;
while (true) {
- char buf[9];
- if (!ReadFdExactly(fd, buf, 8)) {
+ if (!ReadFdExactly(device_fd, buf, 8)) {
fprintf(stderr, "* failed to read command: %s\n", strerror(errno));
return -1;
}
@@ -893,26 +896,35 @@
if (strcmp("DONEDONE", buf) == 0) {
printf("\rTotal xfer: %.2fx%*s\n",
- (double)xfer / (sz ? sz : 1), (int)strlen(fn)+10, "");
+ static_cast<double>(xfer) / (sb.st_size ? sb.st_size : 1),
+ static_cast<int>(strlen(filename) + 10), "");
return 0;
}
int block = strtol(buf, NULL, 10);
size_t offset = block * SIDELOAD_HOST_BLOCK_SIZE;
- if (offset >= sz) {
+ if (offset >= static_cast<size_t>(sb.st_size)) {
fprintf(stderr, "* attempt to read block %d past end\n", block);
return -1;
}
- const uint8_t* start = data + offset;
- size_t offset_end = offset + SIDELOAD_HOST_BLOCK_SIZE;
+
size_t to_write = SIDELOAD_HOST_BLOCK_SIZE;
- if (offset_end > sz) {
- to_write = sz - offset;
+ if ((offset + SIDELOAD_HOST_BLOCK_SIZE) > static_cast<size_t>(sb.st_size)) {
+ to_write = sb.st_size - offset;
}
- if (!WriteFdExactly(fd, start, to_write)) {
- adb_status(fd, &error);
+ if (adb_lseek(package_fd, offset, SEEK_SET) != static_cast<int>(offset)) {
+ fprintf(stderr, "* failed to seek to package block: %s\n", strerror(errno));
+ return -1;
+ }
+ if (!ReadFdExactly(package_fd, buf, to_write)) {
+ fprintf(stderr, "* failed to read package block: %s\n", strerror(errno));
+ return -1;
+ }
+
+ if (!WriteFdExactly(device_fd, buf, to_write)) {
+ adb_status(device_fd, &error);
fprintf(stderr,"* failed to write data '%s' *\n", error.c_str());
return -1;
}
@@ -924,9 +936,9 @@
// extra access to things like the zip central directory).
// This estimate of the completion becomes 100% when we've
// transferred ~2.13 (=100/47) times the package size.
- int percent = (int)(xfer * 47LL / (sz ? sz : 1));
+ int percent = static_cast<int>(xfer * 47LL / (sb.st_size ? sb.st_size : 1));
if (percent != last_percent) {
- printf("\rserving: '%s' (~%d%%) ", fn, percent);
+ printf("\rserving: '%s' (~%d%%) ", filename, percent);
fflush(stdout);
last_percent = percent;
}
diff --git a/adb/sysdeps.h b/adb/sysdeps.h
index f195b4e..f95a855 100644
--- a/adb/sysdeps.h
+++ b/adb/sysdeps.h
@@ -268,10 +268,6 @@
#undef accept
#define accept ___xxx_accept
-int adb_getsockname(int fd, struct sockaddr* sockaddr, socklen_t* optlen);
-#undef getsockname
-#define getsockname(...) ___xxx_getsockname(__VA__ARGS__)
-
// Returns the local port number of a bound socket, or -1 on failure.
int adb_socket_get_local_port(int fd);
diff --git a/adb/sysdeps_win32.cpp b/adb/sysdeps_win32.cpp
index f997e6b..5873b2b 100644
--- a/adb/sysdeps_win32.cpp
+++ b/adb/sysdeps_win32.cpp
@@ -984,7 +984,7 @@
return -1;
}
- int result = (getsockname)(fh->fh_socket, sockaddr, optlen);
+ int result = getsockname(fh->fh_socket, sockaddr, optlen);
if (result == SOCKET_ERROR) {
const DWORD err = WSAGetLastError();
D("adb_getsockname: setsockopt on fd %d failed: %s\n", fd,
@@ -1042,11 +1042,6 @@
int local_port = -1;
std::string error;
- struct sockaddr_storage peer_addr = {};
- struct sockaddr_storage client_addr = {};
- socklen_t peer_socklen = sizeof(peer_addr);
- socklen_t client_socklen = sizeof(client_addr);
-
server = network_loopback_server(0, SOCK_STREAM, &error);
if (server < 0) {
D("adb_socketpair: failed to create server: %s", error.c_str());
@@ -1066,32 +1061,12 @@
goto fail;
}
- // Make sure that the peer that connected to us and the client are the same.
- accepted = adb_socket_accept(server, reinterpret_cast<sockaddr*>(&peer_addr), &peer_socklen);
+ accepted = adb_socket_accept(server, nullptr, nullptr);
if (accepted < 0) {
D("adb_socketpair: failed to accept: %s", strerror(errno));
goto fail;
}
-
- if (adb_getsockname(client, reinterpret_cast<sockaddr*>(&client_addr), &client_socklen) != 0) {
- D("adb_socketpair: failed to getpeername: %s", strerror(errno));
- goto fail;
- }
-
- if (peer_socklen != client_socklen) {
- D("adb_socketpair: client and peer sockaddrs have different lengths");
- errno = EIO;
- goto fail;
- }
-
- if (memcmp(&peer_addr, &client_addr, peer_socklen) != 0) {
- D("adb_socketpair: client and peer sockaddrs don't match");
- errno = EIO;
- goto fail;
- }
-
adb_close(server);
-
sv[0] = client;
sv[1] = accepted;
return 0;
diff --git a/adb/transport_local.cpp b/adb/transport_local.cpp
index 12b98ba..4198a52 100644
--- a/adb/transport_local.cpp
+++ b/adb/transport_local.cpp
@@ -289,7 +289,7 @@
#define open adb_open
#define read adb_read
#define write adb_write
-#include <qemu_pipe.h>
+#include <system/qemu_pipe.h>
#undef open
#undef read
#undef write
diff --git a/base/Android.bp b/base/Android.bp
index 7b1dc8e..3af7686 100644
--- a/base/Android.bp
+++ b/base/Android.bp
@@ -42,24 +42,36 @@
srcs: [
"errors_unix.cpp",
"properties.cpp",
+ "chrono_utils.cpp",
],
cppflags: ["-Wexit-time-destructors"],
sanitize: {
misc_undefined: ["integer"],
},
+
},
darwin: {
- srcs: ["errors_unix.cpp"],
+ srcs: [
+ "chrono_utils.cpp",
+ "errors_unix.cpp",
+ ],
cppflags: ["-Wexit-time-destructors"],
},
linux_bionic: {
- srcs: ["errors_unix.cpp"],
+ srcs: [
+ "chrono_utils.cpp",
+ "errors_unix.cpp",
+ ],
cppflags: ["-Wexit-time-destructors"],
enabled: true,
},
linux: {
- srcs: ["errors_unix.cpp"],
+ srcs: [
+ "chrono_utils.cpp",
+ "errors_unix.cpp",
+ ],
cppflags: ["-Wexit-time-destructors"],
+ host_ldlibs: ["-lrt"],
},
windows: {
srcs: [
@@ -86,17 +98,25 @@
"parseint_test.cpp",
"parsenetaddress_test.cpp",
"quick_exit_test.cpp",
+ "scopeguard_test.cpp",
"stringprintf_test.cpp",
"strings_test.cpp",
"test_main.cpp",
],
target: {
android: {
- srcs: ["properties_test.cpp"],
+ srcs: [
+ "chrono_utils_test.cpp",
+ "properties_test.cpp"
+ ],
sanitize: {
misc_undefined: ["integer"],
},
},
+ linux: {
+ srcs: ["chrono_utils_test.cpp"],
+ host_ldlibs: ["-lrt"],
+ },
windows: {
srcs: ["utf8_test.cpp"],
enabled: true,
diff --git a/base/chrono_utils.cpp b/base/chrono_utils.cpp
new file mode 100644
index 0000000..5eedf3b
--- /dev/null
+++ b/base/chrono_utils.cpp
@@ -0,0 +1,37 @@
+/*
+ * 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 "android-base/chrono_utils.h"
+
+#include <time.h>
+
+namespace android {
+namespace base {
+
+boot_clock::time_point boot_clock::now() {
+#ifdef __ANDROID__
+ timespec ts;
+ clock_gettime(CLOCK_BOOTTIME, &ts);
+ return boot_clock::time_point(std::chrono::seconds(ts.tv_sec) +
+ std::chrono::nanoseconds(ts.tv_nsec));
+#else
+ // Darwin does not support clock_gettime.
+ return boot_clock::time_point();
+#endif // __ANDROID__
+}
+
+} // namespace base
+} // namespace android
diff --git a/base/chrono_utils_test.cpp b/base/chrono_utils_test.cpp
new file mode 100644
index 0000000..057132d
--- /dev/null
+++ b/base/chrono_utils_test.cpp
@@ -0,0 +1,46 @@
+/*
+ * 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 "android-base/chrono_utils.h"
+
+#include <time.h>
+
+#include <chrono>
+
+#include <gtest/gtest.h>
+
+namespace android {
+namespace base {
+
+std::chrono::seconds GetBootTimeSeconds() {
+ struct timespec now;
+ clock_gettime(CLOCK_BOOTTIME, &now);
+
+ auto now_tp = boot_clock::time_point(std::chrono::seconds(now.tv_sec) +
+ std::chrono::nanoseconds(now.tv_nsec));
+ return std::chrono::duration_cast<std::chrono::seconds>(now_tp.time_since_epoch());
+}
+
+// Tests (at least) the seconds accuracy of the boot_clock::now() method.
+TEST(ChronoUtilsTest, BootClockNowSeconds) {
+ auto now = GetBootTimeSeconds();
+ auto boot_seconds =
+ std::chrono::duration_cast<std::chrono::seconds>(boot_clock::now().time_since_epoch());
+ EXPECT_EQ(now, boot_seconds);
+}
+
+} // namespace base
+} // namespace android
\ No newline at end of file
diff --git a/base/file.cpp b/base/file.cpp
index d4e5894..7fbebc5 100644
--- a/base/file.cpp
+++ b/base/file.cpp
@@ -28,8 +28,9 @@
#include <string>
#include <vector>
-#include "android-base/macros.h" // For TEMP_FAILURE_RETRY on Darwin.
#include "android-base/logging.h"
+#include "android-base/macros.h" // For TEMP_FAILURE_RETRY on Darwin.
+#include "android-base/unique_fd.h"
#include "android-base/utf8.h"
#include "utils/Compat.h"
@@ -69,13 +70,11 @@
content->clear();
int flags = O_RDONLY | O_CLOEXEC | O_BINARY | (follow_symlinks ? 0 : O_NOFOLLOW);
- int fd = TEMP_FAILURE_RETRY(open(path.c_str(), flags));
+ android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), flags)));
if (fd == -1) {
return false;
}
- bool result = ReadFdToString(fd, content);
- close(fd);
- return result;
+ return ReadFdToString(fd, content);
}
bool WriteStringToFd(const std::string& content, int fd) {
@@ -106,7 +105,7 @@
bool follow_symlinks) {
int flags = O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_BINARY |
(follow_symlinks ? 0 : O_NOFOLLOW);
- int fd = TEMP_FAILURE_RETRY(open(path.c_str(), flags, mode));
+ android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), flags, mode)));
if (fd == -1) {
PLOG(ERROR) << "android::WriteStringToFile open failed";
return false;
@@ -126,7 +125,6 @@
PLOG(ERROR) << "android::WriteStringToFile write failed";
return CleanUpAfterFailedWrite(path);
}
- close(fd);
return true;
}
#endif
@@ -135,14 +133,11 @@
bool follow_symlinks) {
int flags = O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_BINARY |
(follow_symlinks ? 0 : O_NOFOLLOW);
- int fd = TEMP_FAILURE_RETRY(open(path.c_str(), flags, DEFFILEMODE));
+ android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), flags, DEFFILEMODE)));
if (fd == -1) {
return false;
}
-
- bool result = WriteStringToFd(content, fd);
- close(fd);
- return result || CleanUpAfterFailedWrite(path);
+ return WriteStringToFd(content, fd) || CleanUpAfterFailedWrite(path);
}
bool ReadFully(int fd, void* data, size_t byte_count) {
diff --git a/base/include/android-base/chrono_utils.h b/base/include/android-base/chrono_utils.h
new file mode 100644
index 0000000..0086425
--- /dev/null
+++ b/base/include/android-base/chrono_utils.h
@@ -0,0 +1,37 @@
+/*
+ * 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 ANDROID_BASE_CHRONO_UTILS_H
+#define ANDROID_BASE_CHRONO_UTILS_H
+
+#include <chrono>
+
+namespace android {
+namespace base {
+
+// A std::chrono clock based on CLOCK_BOOTTIME.
+class boot_clock {
+ public:
+ typedef std::chrono::nanoseconds duration;
+ typedef std::chrono::time_point<boot_clock, duration> time_point;
+
+ static time_point now();
+};
+
+} // namespace base
+} // namespace android
+
+#endif // ANDROID_BASE_CHRONO_UTILS_H
diff --git a/base/include/android-base/properties.h b/base/include/android-base/properties.h
index 4de5e57..041586c 100644
--- a/base/include/android-base/properties.h
+++ b/base/include/android-base/properties.h
@@ -62,15 +62,14 @@
// Waits for the system property `key` to have the value `expected_value`.
// Times out after `relative_timeout`.
// Returns true on success, false on timeout.
-bool WaitForProperty(const std::string& key,
- const std::string& expected_value,
- std::chrono::milliseconds relative_timeout);
+bool WaitForProperty(const std::string& key, const std::string& expected_value,
+ std::chrono::milliseconds relative_timeout = std::chrono::milliseconds::max());
// Waits for the system property `key` to be created.
// Times out after `relative_timeout`.
// Returns true on success, false on timeout.
-bool WaitForPropertyCreation(const std::string& key,
- std::chrono::milliseconds relative_timeout);
+bool WaitForPropertyCreation(const std::string& key, std::chrono::milliseconds relative_timeout =
+ std::chrono::milliseconds::max());
} // namespace base
} // namespace android
diff --git a/base/include/android-base/scopeguard.h b/base/include/android-base/scopeguard.h
new file mode 100644
index 0000000..abcf4bc
--- /dev/null
+++ b/base/include/android-base/scopeguard.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_BASE_SCOPEGUARD_H
+#define ANDROID_BASE_SCOPEGUARD_H
+
+#include <utility> // for std::move
+
+namespace android {
+namespace base {
+
+template <typename F>
+class ScopeGuard {
+ public:
+ ScopeGuard(F f) : f_(f), active_(true) {}
+
+ ScopeGuard(ScopeGuard&& that) : f_(std::move(that.f_)), active_(that.active_) {
+ that.active_ = false;
+ }
+
+ ~ScopeGuard() {
+ if (active_) f_();
+ }
+
+ ScopeGuard() = delete;
+ ScopeGuard(const ScopeGuard&) = delete;
+ void operator=(const ScopeGuard&) = delete;
+ void operator=(ScopeGuard&& that) = delete;
+
+ void Disable() { active_ = false; }
+
+ bool active() const { return active_; }
+
+ private:
+ F f_;
+ bool active_;
+};
+
+template <typename T>
+ScopeGuard<T> make_scope_guard(T f) {
+ return ScopeGuard<T>(f);
+}
+
+} // namespace base
+} // namespace android
+
+#endif // ANDROID_BASE_SCOPEGUARD_H
diff --git a/base/properties.cpp b/base/properties.cpp
index 32c0128..816bca0 100644
--- a/base/properties.cpp
+++ b/base/properties.cpp
@@ -101,22 +101,24 @@
}
// TODO: chrono_utils?
-static void DurationToTimeSpec(timespec& ts, std::chrono::nanoseconds d) {
+static void DurationToTimeSpec(timespec& ts, const std::chrono::milliseconds d) {
auto s = std::chrono::duration_cast<std::chrono::seconds>(d);
auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(d - s);
ts.tv_sec = s.count();
ts.tv_nsec = ns.count();
}
+// TODO: boot_clock?
using AbsTime = std::chrono::time_point<std::chrono::steady_clock>;
-static void UpdateTimeSpec(timespec& ts,
- const AbsTime& timeout) {
+static void UpdateTimeSpec(timespec& ts, std::chrono::milliseconds relative_timeout,
+ const AbsTime& start_time) {
auto now = std::chrono::steady_clock::now();
- auto remaining_timeout = std::chrono::duration_cast<std::chrono::nanoseconds>(timeout - now);
- if (remaining_timeout < 0ns) {
+ auto time_elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - start_time);
+ if (time_elapsed >= relative_timeout) {
ts = { 0, 0 };
} else {
+ auto remaining_timeout = relative_timeout - time_elapsed;
DurationToTimeSpec(ts, remaining_timeout);
}
}
@@ -127,11 +129,7 @@
// Returns nullptr on timeout.
static const prop_info* WaitForPropertyCreation(const std::string& key,
const std::chrono::milliseconds& relative_timeout,
- AbsTime& absolute_timeout) {
- // TODO: boot_clock?
- auto now = std::chrono::steady_clock::now();
- absolute_timeout = now + relative_timeout;
-
+ const AbsTime& start_time) {
// Find the property's prop_info*.
const prop_info* pi;
unsigned global_serial = 0;
@@ -139,17 +137,16 @@
// The property doesn't even exist yet.
// Wait for a global change and then look again.
timespec ts;
- UpdateTimeSpec(ts, absolute_timeout);
+ UpdateTimeSpec(ts, relative_timeout, start_time);
if (!__system_property_wait(nullptr, global_serial, &global_serial, &ts)) return nullptr;
}
return pi;
}
-bool WaitForProperty(const std::string& key,
- const std::string& expected_value,
+bool WaitForProperty(const std::string& key, const std::string& expected_value,
std::chrono::milliseconds relative_timeout) {
- AbsTime absolute_timeout;
- const prop_info* pi = WaitForPropertyCreation(key, relative_timeout, absolute_timeout);
+ auto start_time = std::chrono::steady_clock::now();
+ const prop_info* pi = WaitForPropertyCreation(key, relative_timeout, start_time);
if (pi == nullptr) return false;
WaitForPropertyData data;
@@ -162,7 +159,7 @@
if (data.done) return true;
// It didn't, so wait for the property to change before checking again.
- UpdateTimeSpec(ts, absolute_timeout);
+ UpdateTimeSpec(ts, relative_timeout, start_time);
uint32_t unused;
if (!__system_property_wait(pi, data.last_read_serial, &unused, &ts)) return false;
}
@@ -170,8 +167,8 @@
bool WaitForPropertyCreation(const std::string& key,
std::chrono::milliseconds relative_timeout) {
- AbsTime absolute_timeout;
- return (WaitForPropertyCreation(key, relative_timeout, absolute_timeout) != nullptr);
+ auto start_time = std::chrono::steady_clock::now();
+ return (WaitForPropertyCreation(key, relative_timeout, start_time) != nullptr);
}
} // namespace base
diff --git a/base/properties_test.cpp b/base/properties_test.cpp
index 1bbe482..de5f3dc 100644
--- a/base/properties_test.cpp
+++ b/base/properties_test.cpp
@@ -151,6 +151,38 @@
ASSERT_LT(std::chrono::duration_cast<std::chrono::milliseconds>(t1 - t0), 600ms);
}
+TEST(properties, WaitForProperty_MaxTimeout) {
+ std::atomic<bool> flag{false};
+ std::thread thread([&]() {
+ android::base::SetProperty("debug.libbase.WaitForProperty_test", "a");
+ while (!flag) std::this_thread::yield();
+ std::this_thread::sleep_for(500ms);
+ android::base::SetProperty("debug.libbase.WaitForProperty_test", "b");
+ });
+
+ ASSERT_TRUE(android::base::WaitForProperty("debug.libbase.WaitForProperty_test", "a", 1s));
+ flag = true;
+ // Test that this does not immediately return false due to overflow issues with the timeout.
+ ASSERT_TRUE(android::base::WaitForProperty("debug.libbase.WaitForProperty_test", "b"));
+ thread.join();
+}
+
+TEST(properties, WaitForProperty_NegativeTimeout) {
+ std::atomic<bool> flag{false};
+ std::thread thread([&]() {
+ android::base::SetProperty("debug.libbase.WaitForProperty_test", "a");
+ while (!flag) std::this_thread::yield();
+ std::this_thread::sleep_for(500ms);
+ android::base::SetProperty("debug.libbase.WaitForProperty_test", "b");
+ });
+
+ ASSERT_TRUE(android::base::WaitForProperty("debug.libbase.WaitForProperty_test", "a", 1s));
+ flag = true;
+ // Assert that this immediately returns with a negative timeout
+ ASSERT_FALSE(android::base::WaitForProperty("debug.libbase.WaitForProperty_test", "b", -100ms));
+ thread.join();
+}
+
TEST(properties, WaitForPropertyCreation) {
std::thread thread([&]() {
std::this_thread::sleep_for(100ms);
diff --git a/base/scopeguard_test.cpp b/base/scopeguard_test.cpp
new file mode 100644
index 0000000..e11154a
--- /dev/null
+++ b/base/scopeguard_test.cpp
@@ -0,0 +1,46 @@
+/*
+ * 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 "android-base/scopeguard.h"
+
+#include <utility>
+
+#include <gtest/gtest.h>
+
+TEST(scopeguard, normal) {
+ bool guarded_var = true;
+ {
+ auto scopeguard = android::base::make_scope_guard([&guarded_var] { guarded_var = false; });
+ }
+ ASSERT_FALSE(guarded_var);
+}
+
+TEST(scopeguard, disabled) {
+ bool guarded_var = true;
+ {
+ auto scopeguard = android::base::make_scope_guard([&guarded_var] { guarded_var = false; });
+ scopeguard.Disable();
+ }
+ ASSERT_TRUE(guarded_var);
+}
+
+TEST(scopeguard, moved) {
+ int guarded_var = true;
+ auto scopeguard = android::base::make_scope_guard([&guarded_var] { guarded_var = false; });
+ { decltype(scopeguard) new_guard(std::move(scopeguard)); }
+ EXPECT_FALSE(scopeguard.active());
+ ASSERT_FALSE(guarded_var);
+}
diff --git a/bootstat/Android.bp b/bootstat/Android.bp
index f744ad1..bc90a6e 100644
--- a/bootstat/Android.bp
+++ b/bootstat/Android.bp
@@ -16,7 +16,6 @@
bootstat_lib_src_files = [
"boot_event_record_store.cpp",
- "uptime_parser.cpp",
]
cc_defaults {
@@ -75,6 +74,7 @@
// -----------------------------------------------------------------------------
cc_test {
name: "bootstat_tests",
+ test_suites: ["device-tests"],
defaults: ["bootstat_defaults"],
host_supported: true,
static_libs: [
diff --git a/bootstat/AndroidTest.xml b/bootstat/AndroidTest.xml
new file mode 100644
index 0000000..f3783fa
--- /dev/null
+++ b/bootstat/AndroidTest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Config for bootstat_tests">
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="bootstat_tests->/data/local/tmp/bootstat_tests" />
+ </target_preparer>
+ <option name="test-suite-tag" value="apct" />
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="bootstat_tests" />
+ </test>
+</configuration>
\ No newline at end of file
diff --git a/bootstat/boot_event_record_store.cpp b/bootstat/boot_event_record_store.cpp
index 2648594..99d9405 100644
--- a/bootstat/boot_event_record_store.cpp
+++ b/bootstat/boot_event_record_store.cpp
@@ -20,13 +20,16 @@
#include <fcntl.h>
#include <sys/stat.h>
#include <utime.h>
+
+#include <chrono>
#include <cstdlib>
#include <string>
#include <utility>
+
+#include <android-base/chrono_utils.h>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/parseint.h>
-#include "uptime_parser.h"
namespace {
@@ -55,7 +58,9 @@
}
void BootEventRecordStore::AddBootEvent(const std::string& event) {
- AddBootEventWithValue(event, bootstat::ParseUptime());
+ auto uptime = std::chrono::duration_cast<std::chrono::seconds>(
+ android::base::boot_clock::now().time_since_epoch());
+ AddBootEventWithValue(event, uptime.count());
}
// The implementation of AddBootEventValue makes use of the mtime file
diff --git a/bootstat/boot_event_record_store_test.cpp b/bootstat/boot_event_record_store_test.cpp
index 90f6513..d98169b 100644
--- a/bootstat/boot_event_record_store_test.cpp
+++ b/bootstat/boot_event_record_store_test.cpp
@@ -21,15 +21,18 @@
#include <sys/stat.h>
#include <sys/time.h>
#include <unistd.h>
+
+#include <chrono>
#include <cstdint>
#include <cstdlib>
+
+#include <android-base/chrono_utils.h>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/test_utils.h>
#include <android-base/unique_fd.h>
-#include <gtest/gtest.h>
#include <gmock/gmock.h>
-#include "uptime_parser.h"
+#include <gtest/gtest.h>
using testing::UnorderedElementsAreArray;
@@ -89,6 +92,13 @@
rmdir(path.c_str());
}
+// Returns the time in seconds since boot.
+time_t GetUptimeSeconds() {
+ return std::chrono::duration_cast<std::chrono::seconds>(
+ android::base::boot_clock::now().time_since_epoch())
+ .count();
+}
+
class BootEventRecordStoreTest : public ::testing::Test {
public:
BootEventRecordStoreTest() {
@@ -126,7 +136,7 @@
BootEventRecordStore store;
store.SetStorePath(GetStorePathForTesting());
- time_t uptime = bootstat::ParseUptime();
+ time_t uptime = GetUptimeSeconds();
ASSERT_NE(-1, uptime);
store.AddBootEvent("cenozoic");
@@ -141,7 +151,7 @@
BootEventRecordStore store;
store.SetStorePath(GetStorePathForTesting());
- time_t uptime = bootstat::ParseUptime();
+ time_t uptime = GetUptimeSeconds();
ASSERT_NE(-1, uptime);
store.AddBootEvent("cretaceous");
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index a4cc5f2..6f25d96 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -21,6 +21,7 @@
#include <getopt.h>
#include <unistd.h>
+#include <chrono>
#include <cmath>
#include <cstddef>
#include <cstdio>
@@ -30,15 +31,15 @@
#include <string>
#include <vector>
-#include <android/log.h>
+#include <android-base/chrono_utils.h>
#include <android-base/logging.h>
#include <android-base/parseint.h>
#include <android-base/strings.h>
+#include <android/log.h>
#include <cutils/properties.h>
#include <metricslogger/metrics_logger.h>
#include "boot_event_record_store.h"
-#include "uptime_parser.h"
namespace {
@@ -255,7 +256,8 @@
BootEventRecordStore boot_event_store;
BootEventRecordStore::BootEventRecord record;
- time_t uptime = bootstat::ParseUptime();
+ auto uptime = std::chrono::duration_cast<std::chrono::seconds>(
+ android::base::boot_clock::now().time_since_epoch());
time_t current_time_utc = time(nullptr);
if (boot_event_store.GetBootEvent("last_boot_time_utc", &record)) {
@@ -282,22 +284,21 @@
// Log the amount of time elapsed until the device is decrypted, which
// includes the variable amount of time the user takes to enter the
// decryption password.
- boot_event_store.AddBootEventWithValue("boot_decryption_complete", uptime);
+ boot_event_store.AddBootEventWithValue("boot_decryption_complete", uptime.count());
// Subtract the decryption time to normalize the boot cycle timing.
- time_t boot_complete = uptime - record.second;
+ std::chrono::seconds boot_complete = std::chrono::seconds(uptime.count() - record.second);
boot_event_store.AddBootEventWithValue(boot_complete_prefix + "_post_decrypt",
- boot_complete);
-
+ boot_complete.count());
} else {
- boot_event_store.AddBootEventWithValue(boot_complete_prefix + "_no_encryption",
- uptime);
+ boot_event_store.AddBootEventWithValue(boot_complete_prefix + "_no_encryption",
+ uptime.count());
}
// Record the total time from device startup to boot complete, regardless of
// encryption state.
- boot_event_store.AddBootEventWithValue(boot_complete_prefix, uptime);
+ boot_event_store.AddBootEventWithValue(boot_complete_prefix, uptime.count());
RecordInitBootTimeProp(&boot_event_store, "ro.boottime.init");
RecordInitBootTimeProp(&boot_event_store, "ro.boottime.init.selinux");
diff --git a/bootstat/uptime_parser.cpp b/bootstat/uptime_parser.cpp
deleted file mode 100644
index 7c2034c..0000000
--- a/bootstat/uptime_parser.cpp
+++ /dev/null
@@ -1,38 +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.
- */
-
-#include "uptime_parser.h"
-
-#include <time.h>
-#include <cstdlib>
-#include <string>
-#include <android-base/file.h>
-#include <android-base/logging.h>
-
-namespace bootstat {
-
-time_t ParseUptime() {
- std::string uptime_str;
- if (!android::base::ReadFileToString("/proc/uptime", &uptime_str)) {
- PLOG(ERROR) << "Failed to read /proc/uptime";
- return -1;
- }
-
- // Cast intentionally rounds down.
- return static_cast<time_t>(strtod(uptime_str.c_str(), NULL));
-}
-
-} // namespace bootstat
\ No newline at end of file
diff --git a/cpio/mkbootfs.c b/cpio/mkbootfs.c
index b89c395..e52762e 100644
--- a/cpio/mkbootfs.c
+++ b/cpio/mkbootfs.c
@@ -301,6 +301,7 @@
allocated *= 2;
canned_config = (struct fs_config_entry*)realloc(
canned_config, allocated * sizeof(struct fs_config_entry));
+ if (canned_config == NULL) die("failed to reallocate memory");
}
struct fs_config_entry* cc = canned_config + used;
@@ -320,6 +321,7 @@
++allocated;
canned_config = (struct fs_config_entry*)realloc(
canned_config, allocated * sizeof(struct fs_config_entry));
+ if (canned_config == NULL) die("failed to reallocate memory");
}
canned_config[used].name = NULL;
diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp
index 2d6c7f5..4783d6e 100644
--- a/debuggerd/Android.bp
+++ b/debuggerd/Android.bp
@@ -161,7 +161,9 @@
target: {
android: {
srcs: [
+ "client/debuggerd_client_test.cpp",
"debuggerd_test.cpp",
+ "tombstoned_client.cpp",
"util.cpp"
],
},
@@ -171,10 +173,12 @@
"libbacktrace",
"libbase",
"libcutils",
+ "libdebuggerd_client",
],
static_libs: [
- "libdebuggerd"
+ "libdebuggerd",
+ "libc_logging",
],
local_include_dirs: [
diff --git a/debuggerd/client/debuggerd_client.cpp b/debuggerd/client/debuggerd_client.cpp
index f2975d1..2be13c6 100644
--- a/debuggerd/client/debuggerd_client.cpp
+++ b/debuggerd/client/debuggerd_client.cpp
@@ -19,12 +19,14 @@
#include <fcntl.h>
#include <signal.h>
#include <stdlib.h>
+#include <sys/poll.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <chrono>
+#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <android-base/unique_fd.h>
@@ -33,6 +35,8 @@
#include <debuggerd/protocol.h>
#include <debuggerd/util.h>
+using namespace std::chrono_literals;
+
using android::base::unique_fd;
static bool send_signal(pid_t pid, bool backtrace) {
@@ -45,37 +49,44 @@
return true;
}
+template <typename Duration>
+static void populate_timeval(struct timeval* tv, const Duration& duration) {
+ auto seconds = std::chrono::duration_cast<std::chrono::seconds>(duration);
+ auto microseconds = std::chrono::duration_cast<std::chrono::microseconds>(duration - seconds);
+ tv->tv_sec = static_cast<long>(seconds.count());
+ tv->tv_usec = static_cast<long>(microseconds.count());
+}
+
bool debuggerd_trigger_dump(pid_t pid, unique_fd output_fd, DebuggerdDumpType dump_type,
- int timeout_ms) {
+ unsigned int timeout_ms) {
LOG(INFO) << "libdebuggerd_client: started dumping process " << pid;
unique_fd sockfd;
const auto end = std::chrono::steady_clock::now() + std::chrono::milliseconds(timeout_ms);
- auto set_timeout = [timeout_ms, &sockfd, &end]() {
+ auto time_left = [&end]() { return end - std::chrono::steady_clock::now(); };
+ auto set_timeout = [timeout_ms, &time_left](int sockfd) {
if (timeout_ms <= 0) {
- return true;
+ return sockfd;
}
- auto now = std::chrono::steady_clock::now();
- if (now > end) {
- return false;
+ auto remaining = time_left();
+ if (remaining < decltype(remaining)::zero()) {
+ LOG(ERROR) << "libdebuggerd_client: timeout expired";
+ return -1;
}
- auto time_left = std::chrono::duration_cast<std::chrono::microseconds>(end - now);
- auto seconds = std::chrono::duration_cast<std::chrono::seconds>(time_left);
- auto microseconds = std::chrono::duration_cast<std::chrono::microseconds>(time_left - seconds);
- struct timeval timeout = {
- .tv_sec = static_cast<long>(seconds.count()),
- .tv_usec = static_cast<long>(microseconds.count()),
- };
+ struct timeval timeout;
+ populate_timeval(&timeout, remaining);
if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) != 0) {
- return false;
+ PLOG(ERROR) << "libdebuggerd_client: failed to set receive timeout";
+ return -1;
}
if (setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)) != 0) {
- return false;
+ PLOG(ERROR) << "libdebuggerd_client: failed to set send timeout";
+ return -1;
}
- return true;
+ return sockfd;
};
sockfd.reset(socket(AF_LOCAL, SOCK_SEQPACKET, 0));
@@ -84,36 +95,56 @@
return false;
}
- if (!set_timeout()) {
- PLOG(ERROR) << "libdebugger_client: failed to set timeout";
- return false;
- }
-
- if (socket_local_client_connect(sockfd.get(), kTombstonedInterceptSocketName,
+ if (socket_local_client_connect(set_timeout(sockfd.get()), kTombstonedInterceptSocketName,
ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET) == -1) {
PLOG(ERROR) << "libdebuggerd_client: failed to connect to tombstoned";
return false;
}
InterceptRequest req = {.pid = pid };
- if (!set_timeout()) {
+ if (!set_timeout(sockfd)) {
PLOG(ERROR) << "libdebugger_client: failed to set timeout";
+ return false;
}
- if (send_fd(sockfd.get(), &req, sizeof(req), std::move(output_fd)) != sizeof(req)) {
+ // Create an intermediate pipe to pass to the other end.
+ unique_fd pipe_read, pipe_write;
+ if (!Pipe(&pipe_read, &pipe_write)) {
+ PLOG(ERROR) << "libdebuggerd_client: failed to create pipe";
+ return false;
+ }
+
+ if (send_fd(set_timeout(sockfd), &req, sizeof(req), std::move(pipe_write)) != sizeof(req)) {
PLOG(ERROR) << "libdebuggerd_client: failed to send output fd to tombstoned";
return false;
}
- bool backtrace = dump_type == kDebuggerdBacktrace;
- send_signal(pid, backtrace);
-
- if (!set_timeout()) {
- PLOG(ERROR) << "libdebugger_client: failed to set timeout";
+ // Check to make sure we've successfully registered.
+ InterceptResponse response;
+ ssize_t rc =
+ TEMP_FAILURE_RETRY(recv(set_timeout(sockfd.get()), &response, sizeof(response), MSG_TRUNC));
+ if (rc == 0) {
+ LOG(ERROR) << "libdebuggerd_client: failed to read response from tombstoned: timeout reached?";
+ return false;
+ } else if (rc != sizeof(response)) {
+ LOG(ERROR)
+ << "libdebuggerd_client: received packet of unexpected length from tombstoned: expected "
+ << sizeof(response) << ", received " << rc;
+ return false;
}
- InterceptResponse response;
- ssize_t rc = TEMP_FAILURE_RETRY(recv(sockfd.get(), &response, sizeof(response), MSG_TRUNC));
+ if (response.status != InterceptStatus::kRegistered) {
+ LOG(ERROR) << "libdebuggerd_client: unexpected registration response: "
+ << static_cast<int>(response.status);
+ return false;
+ }
+
+ bool backtrace = dump_type == kDebuggerdBacktrace;
+ if (!send_signal(pid, backtrace)) {
+ return false;
+ }
+
+ rc = TEMP_FAILURE_RETRY(recv(set_timeout(sockfd.get()), &response, sizeof(response), MSG_TRUNC));
if (rc == 0) {
LOG(ERROR) << "libdebuggerd_client: failed to read response from tombstoned: timeout reached?";
return false;
@@ -124,9 +155,53 @@
return false;
}
- if (response.success != 1) {
+ if (response.status != InterceptStatus::kStarted) {
response.error_message[sizeof(response.error_message) - 1] = '\0';
LOG(ERROR) << "libdebuggerd_client: tombstoned reported failure: " << response.error_message;
+ return false;
+ }
+
+ // Forward output from the pipe to the output fd.
+ while (true) {
+ auto remaining_ms = std::chrono::duration_cast<std::chrono::milliseconds>(time_left()).count();
+ if (timeout_ms <= 0) {
+ remaining_ms = -1;
+ } else if (remaining_ms < 0) {
+ LOG(ERROR) << "libdebuggerd_client: timeout expired";
+ return false;
+ }
+
+ struct pollfd pfd = {
+ .fd = pipe_read.get(), .events = POLLIN, .revents = 0,
+ };
+
+ rc = poll(&pfd, 1, remaining_ms);
+ if (rc == -1) {
+ if (errno == EINTR) {
+ continue;
+ } else {
+ PLOG(ERROR) << "libdebuggerd_client: error while polling";
+ return false;
+ }
+ } else if (rc == 0) {
+ LOG(ERROR) << "libdebuggerd_client: timeout expired";
+ return false;
+ }
+
+ char buf[1024];
+ rc = TEMP_FAILURE_RETRY(read(pipe_read.get(), buf, sizeof(buf)));
+ if (rc == 0) {
+ // Done.
+ break;
+ } else if (rc == -1) {
+ PLOG(ERROR) << "libdebuggerd_client: error while reading";
+ return false;
+ }
+
+ if (!android::base::WriteFully(output_fd.get(), buf, rc)) {
+ PLOG(ERROR) << "libdebuggerd_client: error while writing";
+ return false;
+ }
}
LOG(INFO) << "libdebuggerd_client: done dumping process " << pid;
diff --git a/debuggerd/client/debuggerd_client_test.cpp b/debuggerd/client/debuggerd_client_test.cpp
new file mode 100644
index 0000000..aff03e5
--- /dev/null
+++ b/debuggerd/client/debuggerd_client_test.cpp
@@ -0,0 +1,111 @@
+/*
+ * Copyright 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 <debuggerd/client.h>
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <chrono>
+#include <thread>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+
+#include <debuggerd/util.h>
+
+using namespace std::chrono_literals;
+using android::base::unique_fd;
+
+TEST(debuggerd_client, race) {
+ static constexpr int THREAD_COUNT = 1024;
+ pid_t forkpid = fork();
+
+ ASSERT_NE(-1, forkpid);
+
+ if (forkpid == 0) {
+ // Spawn a bunch of threads, to make crash_dump take longer.
+ std::vector<std::thread> threads;
+ for (int i = 0; i < THREAD_COUNT; ++i) {
+ threads.emplace_back([]() {
+ while (true) {
+ std::this_thread::sleep_for(60s);
+ }
+ });
+ }
+
+ std::this_thread::sleep_for(60s);
+ exit(0);
+ }
+
+ unique_fd pipe_read, pipe_write;
+ ASSERT_TRUE(Pipe(&pipe_read, &pipe_write));
+
+ // 64 kB should be enough for everyone.
+ constexpr int PIPE_SIZE = 64 * 1024 * 1024;
+ ASSERT_EQ(PIPE_SIZE, fcntl(pipe_read.get(), F_SETPIPE_SZ, PIPE_SIZE));
+
+ // Wait for a bit to let the child spawn all of its threads.
+ std::this_thread::sleep_for(250ms);
+
+ ASSERT_TRUE(debuggerd_trigger_dump(forkpid, std::move(pipe_write), kDebuggerdBacktrace, 10000));
+ // Immediately kill the forked child, to make sure that the dump didn't return early.
+ ASSERT_EQ(0, kill(forkpid, SIGKILL)) << strerror(errno);
+
+ // Check the output.
+ std::string result;
+ ASSERT_TRUE(android::base::ReadFdToString(pipe_read.get(), &result));
+
+ // Look for "----- end <PID> -----"
+ int found_end = 0;
+
+ std::string expected_end = android::base::StringPrintf("----- end %d -----", forkpid);
+
+ std::vector<std::string> lines = android::base::Split(result, "\n");
+ for (const std::string& line : lines) {
+ if (line == expected_end) {
+ ++found_end;
+ }
+ }
+
+ EXPECT_EQ(1, found_end) << "\nOutput: \n" << result;
+}
+
+TEST(debuggerd_client, no_timeout) {
+ unique_fd pipe_read, pipe_write;
+ ASSERT_TRUE(Pipe(&pipe_read, &pipe_write));
+
+ pid_t forkpid = fork();
+ ASSERT_NE(-1, forkpid);
+ if (forkpid == 0) {
+ pipe_write.reset();
+ char dummy;
+ TEMP_FAILURE_RETRY(read(pipe_read.get(), &dummy, sizeof(dummy)));
+ exit(0);
+ }
+
+ pipe_read.reset();
+
+ unique_fd output_read, output_write;
+ ASSERT_TRUE(Pipe(&output_read, &output_write));
+ ASSERT_TRUE(debuggerd_trigger_dump(forkpid, std::move(output_write), kDebuggerdBacktrace, 0));
+}
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp
index 1a27f3f..b705e27 100644
--- a/debuggerd/debuggerd_test.cpp
+++ b/debuggerd/debuggerd_test.cpp
@@ -36,6 +36,7 @@
#include <cutils/sockets.h>
#include <debuggerd/handler.h>
#include <debuggerd/protocol.h>
+#include <debuggerd/tombstoned.h>
#include <debuggerd/util.h>
#include <gtest/gtest.h>
@@ -77,6 +78,54 @@
} \
} while (0)
+static void tombstoned_intercept(pid_t target_pid, unique_fd* intercept_fd, unique_fd* output_fd) {
+ intercept_fd->reset(socket_local_client(kTombstonedInterceptSocketName,
+ ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET));
+ if (intercept_fd->get() == -1) {
+ FAIL() << "failed to contact tombstoned: " << strerror(errno);
+ }
+
+ InterceptRequest req = {.pid = target_pid};
+
+ unique_fd output_pipe_write;
+ if (!Pipe(output_fd, &output_pipe_write)) {
+ FAIL() << "failed to create output pipe: " << strerror(errno);
+ }
+
+ std::string pipe_size_str;
+ int pipe_buffer_size;
+ if (!android::base::ReadFileToString("/proc/sys/fs/pipe-max-size", &pipe_size_str)) {
+ FAIL() << "failed to read /proc/sys/fs/pipe-max-size: " << strerror(errno);
+ }
+
+ pipe_size_str = android::base::Trim(pipe_size_str);
+
+ if (!android::base::ParseInt(pipe_size_str.c_str(), &pipe_buffer_size, 0)) {
+ FAIL() << "failed to parse pipe max size";
+ }
+
+ if (fcntl(output_fd->get(), F_SETPIPE_SZ, pipe_buffer_size) != pipe_buffer_size) {
+ FAIL() << "failed to set pipe size: " << strerror(errno);
+ }
+
+ if (send_fd(intercept_fd->get(), &req, sizeof(req), std::move(output_pipe_write)) != sizeof(req)) {
+ FAIL() << "failed to send output fd to tombstoned: " << strerror(errno);
+ }
+
+ InterceptResponse response;
+ ssize_t rc = TEMP_FAILURE_RETRY(read(intercept_fd->get(), &response, sizeof(response)));
+ if (rc == -1) {
+ FAIL() << "failed to read response from tombstoned: " << strerror(errno);
+ } else if (rc == 0) {
+ FAIL() << "failed to read response from tombstoned (EOF)";
+ } else if (rc != sizeof(response)) {
+ FAIL() << "received packet of unexpected length from tombstoned: expected " << sizeof(response)
+ << ", received " << rc;
+ }
+
+ ASSERT_EQ(InterceptStatus::kRegistered, response.status);
+}
+
class CrasherTest : public ::testing::Test {
public:
pid_t crasher_pid = -1;
@@ -118,38 +167,7 @@
FAIL() << "crasher hasn't been started";
}
- intercept_fd.reset(socket_local_client(kTombstonedInterceptSocketName,
- ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET));
- if (intercept_fd == -1) {
- FAIL() << "failed to contact tombstoned: " << strerror(errno);
- }
-
- InterceptRequest req = {.pid = crasher_pid };
-
- unique_fd output_pipe_write;
- if (!Pipe(output_fd, &output_pipe_write)) {
- FAIL() << "failed to create output pipe: " << strerror(errno);
- }
-
- std::string pipe_size_str;
- int pipe_buffer_size;
- if (!android::base::ReadFileToString("/proc/sys/fs/pipe-max-size", &pipe_size_str)) {
- FAIL() << "failed to read /proc/sys/fs/pipe-max-size: " << strerror(errno);
- }
-
- pipe_size_str = android::base::Trim(pipe_size_str);
-
- if (!android::base::ParseInt(pipe_size_str.c_str(), &pipe_buffer_size, 0)) {
- FAIL() << "failed to parse pipe max size";
- }
-
- if (fcntl(output_fd->get(), F_SETPIPE_SZ, pipe_buffer_size) != pipe_buffer_size) {
- FAIL() << "failed to set pipe size: " << strerror(errno);
- }
-
- if (send_fd(intercept_fd.get(), &req, sizeof(req), std::move(output_pipe_write)) != sizeof(req)) {
- FAIL() << "failed to send output fd to tombstoned: " << strerror(errno);
- }
+ tombstoned_intercept(crasher_pid, &this->intercept_fd, output_fd);
}
void CrasherTest::FinishIntercept(int* result) {
@@ -165,7 +183,7 @@
FAIL() << "received packet of unexpected length from tombstoned: expected " << sizeof(response)
<< ", received " << rc;
} else {
- *result = response.success;
+ *result = response.status == InterceptStatus::kStarted ? 1 : 0;
}
}
@@ -461,6 +479,7 @@
err(1, "failed to drop ambient capabilities");
}
+ pthread_setname_np(pthread_self(), "thread_name");
raise(SIGSYS);
});
@@ -474,15 +493,13 @@
FinishIntercept(&intercept_result);
ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
ConsumeFd(std::move(output_fd), &result);
+ ASSERT_MATCH(result, R"(name: thread_name\s+>>> .+debuggerd_test(32|64) <<<)");
ASSERT_MATCH(result, R"(#00 pc [0-9a-f]+\s+ /system/lib)" ARCH_SUFFIX R"(/libc.so \(tgkill)");
}
TEST(crash_dump, zombie) {
pid_t forkpid = fork();
- int pipefd[2];
- ASSERT_EQ(0, pipe2(pipefd, O_CLOEXEC));
-
pid_t rc;
int status;
@@ -508,3 +525,97 @@
ASSERT_EQ(0, WEXITSTATUS(status));
}
}
+
+TEST(tombstoned, no_notify) {
+ // Do this a few times.
+ for (int i = 0; i < 3; ++i) {
+ pid_t pid = 123'456'789 + i;
+
+ unique_fd intercept_fd, output_fd;
+ tombstoned_intercept(pid, &intercept_fd, &output_fd);
+
+ {
+ unique_fd tombstoned_socket, input_fd;
+ ASSERT_TRUE(tombstoned_connect(pid, &tombstoned_socket, &input_fd));
+ ASSERT_TRUE(android::base::WriteFully(input_fd.get(), &pid, sizeof(pid)));
+ }
+
+ pid_t read_pid;
+ ASSERT_TRUE(android::base::ReadFully(output_fd.get(), &read_pid, sizeof(read_pid)));
+ ASSERT_EQ(read_pid, pid);
+ }
+}
+
+TEST(tombstoned, stress) {
+ // Spawn threads to simultaneously do a bunch of failing dumps and a bunch of successful dumps.
+ static constexpr int kDumpCount = 100;
+
+ std::atomic<bool> start(false);
+ std::vector<std::thread> threads;
+ threads.emplace_back([&start]() {
+ while (!start) {
+ continue;
+ }
+
+ // Use a way out of range pid, to avoid stomping on an actual process.
+ pid_t pid_base = 1'000'000;
+
+ for (int dump = 0; dump < kDumpCount; ++dump) {
+ pid_t pid = pid_base + dump;
+
+ unique_fd intercept_fd, output_fd;
+ tombstoned_intercept(pid, &intercept_fd, &output_fd);
+
+ // Pretend to crash, and then immediately close the socket.
+ unique_fd sockfd(socket_local_client(kTombstonedCrashSocketName,
+ ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET));
+ if (sockfd == -1) {
+ FAIL() << "failed to connect to tombstoned: " << strerror(errno);
+ }
+ TombstonedCrashPacket packet = {};
+ packet.packet_type = CrashPacketType::kDumpRequest;
+ packet.packet.dump_request.pid = pid;
+ if (TEMP_FAILURE_RETRY(write(sockfd, &packet, sizeof(packet))) != sizeof(packet)) {
+ FAIL() << "failed to write to tombstoned: " << strerror(errno);
+ }
+
+ continue;
+ }
+ });
+
+ threads.emplace_back([&start]() {
+ while (!start) {
+ continue;
+ }
+
+ // Use a way out of range pid, to avoid stomping on an actual process.
+ pid_t pid_base = 2'000'000;
+
+ for (int dump = 0; dump < kDumpCount; ++dump) {
+ pid_t pid = pid_base + dump;
+
+ unique_fd intercept_fd, output_fd;
+ tombstoned_intercept(pid, &intercept_fd, &output_fd);
+
+ {
+ unique_fd tombstoned_socket, input_fd;
+ ASSERT_TRUE(tombstoned_connect(pid, &tombstoned_socket, &input_fd));
+ ASSERT_TRUE(android::base::WriteFully(input_fd.get(), &pid, sizeof(pid)));
+ tombstoned_notify_completion(tombstoned_socket.get());
+ }
+
+ // TODO: Fix the race that requires this sleep.
+ std::this_thread::sleep_for(50ms);
+
+ pid_t read_pid;
+ ASSERT_TRUE(android::base::ReadFully(output_fd.get(), &read_pid, sizeof(read_pid)));
+ ASSERT_EQ(read_pid, pid);
+ }
+ });
+
+ start = true;
+
+ for (std::thread& thread : threads) {
+ thread.join();
+ }
+}
diff --git a/debuggerd/handler/debuggerd_handler.cpp b/debuggerd/handler/debuggerd_handler.cpp
index cf24d57..cd00dc5 100644
--- a/debuggerd/handler/debuggerd_handler.cpp
+++ b/debuggerd/handler/debuggerd_handler.cpp
@@ -120,7 +120,7 @@
}
if (signum == DEBUGGER_SIGNAL) {
- __libc_format_log(ANDROID_LOG_FATAL, "libc", "Requested dump for tid %d (%s)", gettid(),
+ __libc_format_log(ANDROID_LOG_INFO, "libc", "Requested dump for tid %d (%s)", gettid(),
thread_name);
return;
}
diff --git a/debuggerd/include/debuggerd/client.h b/debuggerd/include/debuggerd/client.h
index 91f143b..01de57b 100644
--- a/debuggerd/include/debuggerd/client.h
+++ b/debuggerd/include/debuggerd/client.h
@@ -28,9 +28,9 @@
};
// Trigger a dump of specified process to output_fd.
-// output_fd is *not* consumed, timeouts <= 0 will wait forever.
+// output_fd is consumed, timeout of 0 will wait forever.
bool debuggerd_trigger_dump(pid_t pid, android::base::unique_fd output_fd,
- enum DebuggerdDumpType dump_type, int timeout_ms);
+ enum DebuggerdDumpType dump_type, unsigned int timeout_ms);
int dump_backtrace_to_file(pid_t tid, int fd);
int dump_backtrace_to_file_timeout(pid_t tid, int fd, int timeout_secs);
diff --git a/debuggerd/include/debuggerd/protocol.h b/debuggerd/include/debuggerd/protocol.h
index bb2ab0d..0756876 100644
--- a/debuggerd/include/debuggerd/protocol.h
+++ b/debuggerd/include/debuggerd/protocol.h
@@ -56,8 +56,14 @@
int32_t pid;
};
+enum class InterceptStatus : uint8_t {
+ kFailed,
+ kStarted,
+ kRegistered,
+};
+
// Sent either immediately upon failure, or when the intercept has been used.
struct InterceptResponse {
- uint8_t success; // 0 or 1
+ InterceptStatus status;
char error_message[127]; // always null-terminated
};
diff --git a/debuggerd/tombstoned/intercept_manager.cpp b/debuggerd/tombstoned/intercept_manager.cpp
index 789260d..dff942c 100644
--- a/debuggerd/tombstoned/intercept_manager.cpp
+++ b/debuggerd/tombstoned/intercept_manager.cpp
@@ -105,6 +105,7 @@
// We trust the other side, so only do minimal validity checking.
if (intercept_request.pid <= 0 || intercept_request.pid > std::numeric_limits<pid_t>::max()) {
InterceptResponse response = {};
+ response.status = InterceptStatus::kFailed;
snprintf(response.error_message, sizeof(response.error_message), "invalid pid %" PRId32,
intercept_request.pid);
TEMP_FAILURE_RETRY(write(sockfd, &response, sizeof(response)));
@@ -113,9 +114,10 @@
intercept->intercept_pid = intercept_request.pid;
- // Register the intercept with the InterceptManager.
+ // Check if it's already registered.
if (intercept_manager->intercepts.count(intercept_request.pid) > 0) {
InterceptResponse response = {};
+ response.status = InterceptStatus::kFailed;
snprintf(response.error_message, sizeof(response.error_message),
"pid %" PRId32 " already intercepted", intercept_request.pid);
TEMP_FAILURE_RETRY(write(sockfd, &response, sizeof(response)));
@@ -123,6 +125,15 @@
goto fail;
}
+ // Let the other side know that the intercept has been registered, now that we know we can't
+ // fail. tombstoned is single threaded, so this isn't racy.
+ InterceptResponse response = {};
+ response.status = InterceptStatus::kRegistered;
+ if (TEMP_FAILURE_RETRY(write(sockfd, &response, sizeof(response))) == -1) {
+ PLOG(WARNING) << "failed to notify interceptor of registration";
+ goto fail;
+ }
+
intercept->output_fd = std::move(rcv_fd);
intercept_manager->intercepts[intercept_request.pid] = std::unique_ptr<Intercept>(intercept);
intercept->registered = true;
@@ -174,7 +185,7 @@
LOG(INFO) << "found intercept fd " << intercept->output_fd.get() << " for pid " << pid;
InterceptResponse response = {};
- response.success = 1;
+ response.status = InterceptStatus::kStarted;
TEMP_FAILURE_RETRY(write(intercept->sockfd, &response, sizeof(response)));
*out_fd = std::move(intercept->output_fd);
diff --git a/debuggerd/tombstoned/tombstoned.cpp b/debuggerd/tombstoned/tombstoned.cpp
index 6754508..2248a21 100644
--- a/debuggerd/tombstoned/tombstoned.cpp
+++ b/debuggerd/tombstoned/tombstoned.cpp
@@ -126,9 +126,7 @@
return result;
}
-static void dequeue_request(Crash* crash) {
- ++num_concurrent_dumps;
-
+static void perform_request(Crash* crash) {
unique_fd output_fd;
if (!intercept_manager->GetIntercept(crash->crash_pid, &output_fd)) {
output_fd = get_tombstone_fd();
@@ -153,12 +151,22 @@
crash_completed_cb, crash);
event_add(crash->crash_event, &timeout);
}
+
+ ++num_concurrent_dumps;
return;
fail:
delete crash;
}
+static void dequeue_requests() {
+ while (!queued_requests.empty() && num_concurrent_dumps < kMaxConcurrentDumps) {
+ Crash* next_crash = queued_requests.front();
+ queued_requests.pop_front();
+ perform_request(next_crash);
+ }
+}
+
static void crash_accept_cb(evconnlistener* listener, evutil_socket_t sockfd, sockaddr*, int,
void*) {
event_base* base = evconnlistener_get_base(listener);
@@ -207,7 +215,7 @@
LOG(INFO) << "enqueueing crash request for pid " << crash->crash_pid;
queued_requests.push_back(crash);
} else {
- dequeue_request(crash);
+ perform_request(crash);
}
return;
@@ -247,11 +255,7 @@
delete crash;
// If there's something queued up, let them proceed.
- if (!queued_requests.empty()) {
- Crash* next_crash = queued_requests.front();
- queued_requests.pop_front();
- dequeue_request(next_crash);
- }
+ dequeue_requests();
}
int main(int, char* []) {
diff --git a/demangle/demangle.cpp b/demangle/demangle.cpp
index be4d2dd..66e5e58 100644
--- a/demangle/demangle.cpp
+++ b/demangle/demangle.cpp
@@ -20,22 +20,25 @@
#include <string.h>
#include <unistd.h>
+#include <cctype>
#include <string>
#include <demangle.h>
extern "C" char* __cxa_demangle(const char*, char*, size_t*, int*);
-void usage(const char* prog_name) {
- printf("Usage: %s [-c] <NAME_TO_DEMANGLE>\n", prog_name);
- printf(" -c\n");
- printf(" Compare the results of __cxa_demangle against the current\n");
- printf(" demangler.\n");
+static void Usage(const char* prog_name) {
+ printf("usage: %s [-c] [NAME_TO_DEMANGLE...]\n", prog_name);
+ printf("\n");
+ printf("Demangles C++ mangled names if supplied on the command-line, or found\n");
+ printf("reading from stdin otherwise.\n");
+ printf("\n");
+ printf("-c\tCompare against __cxa_demangle\n");
+ printf("\n");
}
-std::string DemangleWithCxa(const char* name) {
+static std::string DemangleWithCxa(const char* name) {
const char* cxa_demangle = __cxa_demangle(name, nullptr, nullptr, nullptr);
-
if (cxa_demangle == nullptr) {
return name;
}
@@ -54,6 +57,49 @@
return demangled_str;
}
+static void Compare(const char* name, const std::string& demangled_name) {
+ std::string cxa_demangled_name(DemangleWithCxa(name));
+ if (cxa_demangled_name != demangled_name) {
+ printf("\nMismatch!\n");
+ printf("\tmangled name: %s\n", name);
+ printf("\tour demangle: %s\n", demangled_name.c_str());
+ printf("\tcxa demangle: %s\n", cxa_demangled_name.c_str());
+ exit(1);
+ }
+}
+
+static int Filter(bool compare) {
+ char* line = nullptr;
+ size_t line_length = 0;
+
+ while ((getline(&line, &line_length, stdin)) != -1) {
+ char* p = line;
+ char* name;
+ while ((name = strstr(p, "_Z")) != nullptr) {
+ // Output anything before the identifier.
+ *name = 0;
+ printf("%s", p);
+ *name = '_';
+
+ // Extract the identifier.
+ p = name;
+ while (*p && (std::isalnum(*p) || *p == '_' || *p == '.' || *p == '$')) ++p;
+
+ // Demangle and output.
+ std::string identifier(name, p);
+ std::string demangled_name = demangle(identifier.c_str());
+ printf("%s", demangled_name.c_str());
+
+ if (compare) Compare(identifier.c_str(), demangled_name);
+ }
+ // Output anything after the last identifier.
+ printf("%s", p);
+ }
+
+ free(line);
+ return 0;
+}
+
int main(int argc, char** argv) {
#ifdef __BIONIC__
const char* prog_name = getprogname();
@@ -67,31 +113,21 @@
if (opt_char == 'c') {
compare = true;
} else {
- usage(prog_name);
+ Usage(prog_name);
return 1;
}
}
- if (optind >= argc || argc - optind != 1) {
- printf("Must supply a single argument.\n\n");
- usage(prog_name);
- return 1;
- }
- const char* name = argv[optind];
- std::string demangled_name = demangle(name);
+ // With no arguments, act as a filter.
+ if (optind == argc) return Filter(compare);
- printf("%s\n", demangled_name.c_str());
+ // Otherwise demangle each argument.
+ while (optind < argc) {
+ const char* name = argv[optind++];
+ std::string demangled_name = demangle(name);
+ printf("%s\n", demangled_name.c_str());
- if (compare) {
- std::string cxa_demangle_str(DemangleWithCxa(name));
-
- if (cxa_demangle_str != demangled_name) {
- printf("Mismatch\n");
- printf("cxa demangle: %s\n", cxa_demangle_str.c_str());
- return 1;
- } else {
- printf("Match\n");
- }
+ if (compare) Compare(name, demangled_name);
}
return 0;
}
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 2927b16..704dc43 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -115,6 +115,7 @@
{"system_other.img", "system.sig", "system", true, true},
{"vendor.img", "vendor.sig", "vendor", true, false},
{"vendor_other.img", "vendor.sig", "vendor", true, true},
+ {"vbmeta.img", "vbmeta.sig", "vbmeta", true, false},
};
static std::string find_item_given_name(const char* img_name, const char* product) {
@@ -144,6 +145,8 @@
fn = "system.img";
} else if(!strcmp(item,"vendor")) {
fn = "vendor.img";
+ } else if(!strcmp(item,"vbmeta")) {
+ fn = "vbmeta.img";
} else if(!strcmp(item,"userdata")) {
fn = "userdata.img";
} else if(!strcmp(item,"cache")) {
@@ -1536,6 +1539,7 @@
setvbuf(stderr, nullptr, _IONBF, 0);
} else if (strcmp("version", longopts[longindex].name) == 0) {
fprintf(stdout, "fastboot version %s\n", FASTBOOT_REVISION);
+ fprintf(stdout, "Installed as %s\n", android::base::GetExecutablePath().c_str());
return 0;
} else if (strcmp("slot", longopts[longindex].name) == 0) {
slot_override = std::string(optarg);
diff --git a/fingerprintd/FingerprintDaemonProxy.cpp b/fingerprintd/FingerprintDaemonProxy.cpp
index 1c7da30..b3c0cd7 100644
--- a/fingerprintd/FingerprintDaemonProxy.cpp
+++ b/fingerprintd/FingerprintDaemonProxy.cpp
@@ -31,7 +31,7 @@
FingerprintDaemonProxy* FingerprintDaemonProxy::sInstance = NULL;
// Supported fingerprint HAL version
-static const uint16_t kVersion = HARDWARE_MODULE_API_VERSION(2, 0);
+static const uint16_t kVersion = HARDWARE_MODULE_API_VERSION(2, 1);
FingerprintDaemonProxy::FingerprintDaemonProxy() : mModule(NULL), mDevice(NULL), mCallback(NULL) {
diff --git a/fs_mgr/fs_mgr_avb.cpp b/fs_mgr/fs_mgr_avb.cpp
index 2cb7e34..7512eb9 100644
--- a/fs_mgr/fs_mgr_avb.cpp
+++ b/fs_mgr/fs_mgr_avb.cpp
@@ -390,10 +390,12 @@
continue;
}
- // Ensures that hashtree descriptor is either in /vbmeta or in
+ // Ensures that hashtree descriptor is in /vbmeta or /boot or in
// the same partition for verity setup.
std::string vbmeta_partition_name(verify_data.vbmeta_images[i].partition_name);
- if (vbmeta_partition_name != "vbmeta" && vbmeta_partition_name != partition_name) {
+ if (vbmeta_partition_name != "vbmeta" &&
+ vbmeta_partition_name != "boot" && // for legacy device to append top-level vbmeta
+ vbmeta_partition_name != partition_name) {
LWARNING << "Skip vbmeta image at " << verify_data.vbmeta_images[i].partition_name
<< " for partition: " << partition_name.c_str();
continue;
@@ -454,12 +456,12 @@
// be returned when there is an error.
std::string hash_alg;
- if (fs_mgr_get_boot_config("vbmeta.hash_alg", &hash_alg) == 0) {
- if (hash_alg == "sha256" || hash_alg == "sha512") {
- return true;
- }
+ if (!fs_mgr_get_boot_config("vbmeta.hash_alg", &hash_alg)) {
+ return false;
}
-
+ if (hash_alg == "sha256" || hash_alg == "sha512") {
+ return true;
+ }
return false;
}
@@ -489,7 +491,13 @@
// fs_mgr only deals with HASHTREE partitions.
const char *requested_partitions[] = {nullptr};
std::string ab_suffix;
- fs_mgr_get_boot_config("slot_suffix", &ab_suffix);
+ std::string slot;
+ if (fs_mgr_get_boot_config("slot", &slot)) {
+ ab_suffix = "_" + slot;
+ } else {
+ // remove slot_suffix once bootloaders update to new androidboot.slot param
+ fs_mgr_get_boot_config("slot_suffix", &ab_suffix);
+ }
AvbSlotVerifyResult verify_result =
avb_slot_verify(fs_mgr_avb_ops, requested_partitions, ab_suffix.c_str(),
fs_mgr_vbmeta_prop.allow_verification_error, &fs_mgr_avb_verify_data);
diff --git a/fs_mgr/fs_mgr_avb_ops.cpp b/fs_mgr/fs_mgr_avb_ops.cpp
index dd8c7e7..8e49663 100644
--- a/fs_mgr/fs_mgr_avb_ops.cpp
+++ b/fs_mgr/fs_mgr_avb_ops.cpp
@@ -39,7 +39,28 @@
#include "fs_mgr_avb_ops.h"
#include "fs_mgr_priv.h"
-static struct fstab* fs_mgr_fstab = nullptr;
+static std::string fstab_by_name_prefix;
+
+static std::string extract_by_name_prefix(struct fstab* fstab) {
+ // In AVB, we can assume that there's an entry for the /misc mount
+ // point in the fstab file and use that to get the device file for
+ // the misc partition. The device needs not to have an actual /misc
+ // partition. Then returns the prefix by removing the trailing "misc":
+ //
+ // - /dev/block/platform/soc.0/7824900.sdhci/by-name/misc ->
+ // - /dev/block/platform/soc.0/7824900.sdhci/by-name/
+
+ struct fstab_rec* fstab_entry = fs_mgr_get_entry_for_mount_point(fstab, "/misc");
+ if (fstab_entry == nullptr) {
+ LERROR << "/misc mount point not found in fstab";
+ return "";
+ }
+
+ std::string full_path(fstab_entry->blk_device);
+ size_t end_slash = full_path.find_last_of("/");
+
+ return full_path.substr(0, end_slash + 1);
+}
static AvbIOResult read_from_partition(AvbOps* ops ATTRIBUTE_UNUSED, const char* partition,
int64_t offset, size_t num_bytes, void* buffer,
@@ -49,36 +70,20 @@
// for partitions having 'slotselect' optin in fstab file, but it
// won't be appended to the mount point.
//
- // In AVB, we can assume that there's an entry for the /misc mount
- // point and use that to get the device file for the misc partition.
- // From there we'll assume that a by-name scheme is used
- // so we can just replace the trailing "misc" by the given
- // |partition|, e.g.
+ // Appends |partition| to the fstab_by_name_prefix, which is obtained
+ // by removing the trailing "misc" from the device file of /misc mount
+ // point. e.g.,
//
- // - /dev/block/platform/soc.0/7824900.sdhci/by-name/misc ->
+ // - /dev/block/platform/soc.0/7824900.sdhci/by-name/ ->
// - /dev/block/platform/soc.0/7824900.sdhci/by-name/system_a
- struct fstab_rec* fstab_entry = fs_mgr_get_entry_for_mount_point(fs_mgr_fstab, "/misc");
-
- if (fstab_entry == nullptr) {
- LERROR << "/misc mount point not found in fstab";
- return AVB_IO_RESULT_ERROR_IO;
- }
-
- std::string partition_name(partition);
- std::string path(fstab_entry->blk_device);
- // Replaces the last field of device file if it's not misc.
- if (!android::base::StartsWith(partition_name, "misc")) {
- size_t end_slash = path.find_last_of("/");
- std::string by_name_prefix(path.substr(0, end_slash + 1));
- path = by_name_prefix + partition_name;
- }
+ std::string path = fstab_by_name_prefix + partition;
// Ensures the device path (a symlink created by init) is ready to
// access. fs_mgr_test_access() will test a few iterations if the
// path doesn't exist yet.
if (fs_mgr_test_access(path.c_str()) < 0) {
- return AVB_IO_RESULT_ERROR_IO;
+ return AVB_IO_RESULT_ERROR_NO_SUCH_PARTITION;
}
android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC)));
@@ -168,8 +173,8 @@
AvbOps* fs_mgr_dummy_avb_ops_new(struct fstab* fstab) {
AvbOps* ops;
- // Assigns the fstab to the static variable for later use.
- fs_mgr_fstab = fstab;
+ fstab_by_name_prefix = extract_by_name_prefix(fstab);
+ if (fstab_by_name_prefix.empty()) return nullptr;
ops = (AvbOps*)calloc(1, sizeof(AvbOps));
if (ops == nullptr) {
diff --git a/fs_mgr/fs_mgr_boot_config.cpp b/fs_mgr/fs_mgr_boot_config.cpp
index 5b2f218..cffa6ce 100644
--- a/fs_mgr/fs_mgr_boot_config.cpp
+++ b/fs_mgr/fs_mgr_boot_config.cpp
@@ -56,7 +56,7 @@
return true;
}
- LERROR << "Error finding '" << key << "' in device tree";
+ LINFO << "Error finding '" << key << "' in device tree";
}
return false;
diff --git a/fs_mgr/fs_mgr_fstab.cpp b/fs_mgr/fs_mgr_fstab.cpp
index a4f88ba..0406efd 100644
--- a/fs_mgr/fs_mgr_fstab.cpp
+++ b/fs_mgr/fs_mgr_fstab.cpp
@@ -625,17 +625,6 @@
return fstab;
}
-/* combines fstab entries passed in from device tree with
- * the ones found from fstab_path
- */
-struct fstab *fs_mgr_read_fstab_with_dt(const char *fstab_path)
-{
- struct fstab *fstab_dt = fs_mgr_read_fstab_dt();
- struct fstab *fstab = fs_mgr_read_fstab(fstab_path);
-
- return in_place_merge(fstab_dt, fstab);
-}
-
/*
* tries to load default fstab.<hardware> file from /odm/etc, /vendor/etc
* or /. loads the first one found and also combines fstab entries passed
@@ -646,7 +635,10 @@
std::string hw;
std::string default_fstab;
- if (fs_mgr_get_boot_config("hardware", &hw)) {
+ // Use different fstab paths for normal boot and recovery boot, respectively
+ if (access("/sbin/recovery", F_OK) == 0) {
+ default_fstab = "/etc/recovery.fstab";
+ } else if (fs_mgr_get_boot_config("hardware", &hw)) { // normal boot
for (const char *prefix : {"/odm/etc/fstab.","/vendor/etc/fstab.", "/fstab."}) {
default_fstab = prefix + hw;
if (access(default_fstab.c_str(), F_OK) == 0) break;
@@ -655,7 +647,12 @@
LWARNING << __FUNCTION__ << "(): failed to find device hardware name";
}
- return fs_mgr_read_fstab_with_dt(default_fstab.c_str());
+ // combines fstab entries passed in from device tree with
+ // the ones found from default_fstab file
+ struct fstab *fstab_dt = fs_mgr_read_fstab_dt();
+ struct fstab *fstab = fs_mgr_read_fstab(default_fstab.c_str());
+
+ return in_place_merge(fstab_dt, fstab);
}
void fs_mgr_free_fstab(struct fstab *fstab)
diff --git a/fs_mgr/fs_mgr_slotselect.cpp b/fs_mgr/fs_mgr_slotselect.cpp
index a536b22..7a45473 100644
--- a/fs_mgr/fs_mgr_slotselect.cpp
+++ b/fs_mgr/fs_mgr_slotselect.cpp
@@ -31,15 +31,16 @@
char *tmp;
if (!got_suffix) {
- if (!fs_mgr_get_boot_config("slot_suffix", &suffix)) {
- return -1;
+ std::string slot;
+ if (fs_mgr_get_boot_config("slot", &slot)) {
+ suffix = "_" + slot;
+ } else if (!fs_mgr_get_boot_config("slot_suffix", &suffix)) {
+ // remove slot_suffix once bootloaders update to new androidboot.slot param
+ return -1;
}
- got_suffix = 1;
- // remove below line when bootloaders fix androidboot.slot_suffix param
- if (suffix[0] == '_') suffix.erase(suffix.begin());
}
- if (asprintf(&tmp, "%s_%s", fstab->recs[n].blk_device, suffix.c_str()) > 0) {
+ if (asprintf(&tmp, "%s%s", fstab->recs[n].blk_device, suffix.c_str()) > 0) {
free(fstab->recs[n].blk_device);
fstab->recs[n].blk_device = tmp;
} else {
diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h
index e4aeb48..4b626de 100644
--- a/fs_mgr/include/fs_mgr.h
+++ b/fs_mgr/include/fs_mgr.h
@@ -88,7 +88,6 @@
struct fstab *fs_mgr_read_fstab_default();
struct fstab *fs_mgr_read_fstab_dt();
struct fstab *fs_mgr_read_fstab(const char *fstab_path);
-struct fstab *fs_mgr_read_fstab_with_dt(const char *fstab_path);
void fs_mgr_free_fstab(struct fstab *fstab);
#define FS_MGR_MNTALL_DEV_FILE_ENCRYPTED 5
diff --git a/gatekeeperd/gatekeeperd.cpp b/gatekeeperd/gatekeeperd.cpp
index d4a92e5..96bda07 100644
--- a/gatekeeperd/gatekeeperd.cpp
+++ b/gatekeeperd/gatekeeperd.cpp
@@ -166,7 +166,14 @@
enrolled_password_handle, enrolled_password_handle_length);
}
- if (ret == 0) {
+ if (ret == GATEKEEPER_RESPONSE_OK && (*enrolled_password_handle == nullptr ||
+ *enrolled_password_handle_length != sizeof(password_handle_t))) {
+ ret = GATEKEEPER_RESPONSE_ERROR;
+ ALOGE("HAL: password_handle=%p size_of_handle=%" PRIu32 "\n",
+ *enrolled_password_handle, *enrolled_password_handle_length);
+ }
+
+ if (ret == GATEKEEPER_RESPONSE_OK) {
gatekeeper::password_handle_t *handle =
reinterpret_cast<gatekeeper::password_handle_t *>(*enrolled_password_handle);
store_sid(uid, handle->user_id);
diff --git a/healthd/Android.mk b/healthd/Android.mk
index 7c5e35b..b292725 100644
--- a/healthd/Android.mk
+++ b/healthd/Android.mk
@@ -21,6 +21,10 @@
include $(BUILD_STATIC_LIBRARY)
include $(CLEAR_VARS)
+ifeq ($(strip $(BOARD_CHARGER_ENABLE_SUSPEND)),true)
+LOCAL_CFLAGS += -DCHARGER_ENABLE_SUSPEND
+LOCAL_SHARED_LIBRARIES += libsuspend
+endif
LOCAL_SRC_FILES := \
healthd_mode_android.cpp \
healthd_mode_charger.cpp \
@@ -90,6 +94,14 @@
LOCAL_C_INCLUDES := bootable/recovery $(LOCAL_PATH)/include
+ifneq ($(BOARD_PERIODIC_CHORES_INTERVAL_FAST),)
+LOCAL_CFLAGS += -DBOARD_PERIODIC_CHORES_INTERVAL_FAST=$(BOARD_PERIODIC_CHORES_INTERVAL_FAST)
+endif
+
+ifneq ($(BOARD_PERIODIC_CHORES_INTERVAL_SLOW),)
+LOCAL_CFLAGS += -DBOARD_PERIODIC_CHORES_INTERVAL_SLOW=$(BOARD_PERIODIC_CHORES_INTERVAL_SLOW)
+endif
+
LOCAL_STATIC_LIBRARIES := \
libhealthd_internal \
libbatterymonitor \
diff --git a/healthd/healthd.cpp b/healthd/healthd.cpp
index 20a6bf6..aa6735d 100644
--- a/healthd/healthd.cpp
+++ b/healthd/healthd.cpp
@@ -35,9 +35,19 @@
using namespace android;
-// Periodic chores intervals in seconds
-#define DEFAULT_PERIODIC_CHORES_INTERVAL_FAST (60 * 1)
-#define DEFAULT_PERIODIC_CHORES_INTERVAL_SLOW (60 * 10)
+#ifndef BOARD_PERIODIC_CHORES_INTERVAL_FAST
+ // Periodic chores fast interval in seconds
+ #define DEFAULT_PERIODIC_CHORES_INTERVAL_FAST (60 * 1)
+#else
+ #define DEFAULT_PERIODIC_CHORES_INTERVAL_FAST (BOARD_PERIODIC_CHORES_INTERVAL_FAST)
+#endif
+
+#ifndef BOARD_PERIODIC_CHORES_INTERVAL_SLOW
+ // Periodic chores fast interval in seconds
+ #define DEFAULT_PERIODIC_CHORES_INTERVAL_SLOW (60 * 10)
+#else
+ #define DEFAULT_PERIODIC_CHORES_INTERVAL_SLOW (BOARD_PERIODIC_CHORES_INTERVAL_SLOW)
+#endif
static struct healthd_config healthd_config = {
.periodic_chores_interval_fast = DEFAULT_PERIODIC_CHORES_INTERVAL_FAST,
@@ -143,10 +153,14 @@
struct android::BatteryProperties* /*props*/) {
}
-int healthd_register_event(int fd, void (*handler)(uint32_t)) {
+int healthd_register_event(int fd, void (*handler)(uint32_t), EventWakeup wakeup) {
struct epoll_event ev;
- ev.events = EPOLLIN | EPOLLWAKEUP;
+ ev.events = EPOLLIN;
+
+ if (wakeup == EVENT_WAKEUP_FD)
+ ev.events |= EPOLLWAKEUP;
+
ev.data.ptr = (void *)handler;
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev) == -1) {
KLOG_ERROR(LOG_TAG,
@@ -252,7 +266,7 @@
}
fcntl(uevent_fd, F_SETFL, O_NONBLOCK);
- if (healthd_register_event(uevent_fd, uevent_event))
+ if (healthd_register_event(uevent_fd, uevent_event, EVENT_WAKEUP_FD))
KLOG_ERROR(LOG_TAG,
"register for uevent events failed\n");
}
@@ -275,7 +289,7 @@
return;
}
- if (healthd_register_event(wakealarm_fd, wakealarm_event))
+ if (healthd_register_event(wakealarm_fd, wakealarm_event, EVENT_WAKEUP_FD))
KLOG_ERROR(LOG_TAG,
"Registration of wakealarm event failed\n");
@@ -283,17 +297,22 @@
}
static void healthd_mainloop(void) {
+ int nevents = 0;
while (1) {
struct epoll_event events[eventct];
- int nevents;
int timeout = awake_poll_interval;
int mode_timeout;
+ /* Don't wait for first timer timeout to run periodic chores */
+ if (!nevents)
+ periodic_chores();
+
+ healthd_mode_ops->heartbeat();
+
mode_timeout = healthd_mode_ops->preparetowait();
if (timeout < 0 || (mode_timeout > 0 && mode_timeout < timeout))
timeout = mode_timeout;
nevents = epoll_wait(epollfd, events, eventct, timeout);
-
if (nevents == -1) {
if (errno == EINTR)
continue;
@@ -305,11 +324,6 @@
if (events[n].data.ptr)
(*(void (*)(int))events[n].data.ptr)(events[n].events);
}
-
- if (!nevents)
- periodic_chores();
-
- healthd_mode_ops->heartbeat();
}
return;
diff --git a/healthd/healthd_mode_charger.cpp b/healthd/healthd_mode_charger.cpp
index 2f69372..91774c6 100644
--- a/healthd/healthd_mode_charger.cpp
+++ b/healthd/healthd_mode_charger.cpp
@@ -343,14 +343,19 @@
static void draw_percent(const animation& anim)
{
- if (anim.cur_level <= 0 || anim.cur_status != BATTERY_STATUS_CHARGING) return;
+ int cur_level = anim.cur_level;
+ if (anim.cur_status == BATTERY_STATUS_FULL) {
+ cur_level = 100;
+ }
+
+ if (cur_level <= 0) return;
const animation::text_field& field = anim.text_percent;
if (field.font == nullptr || field.font->char_width == 0 || field.font->char_height == 0) {
return;
}
- std::string str = base::StringPrintf("%d%%", anim.cur_level);
+ std::string str = base::StringPrintf("%d%%", cur_level);
int x, y;
determine_xy(field, str.size(), &x, &y);
@@ -840,7 +845,7 @@
std::placeholders::_2));
if (!ret) {
epollfd = ev_get_epollfd();
- healthd_register_event(epollfd, charger_event_handler);
+ healthd_register_event(epollfd, charger_event_handler, EVENT_WAKEUP_FD);
}
struct animation* anim = init_animation();
diff --git a/healthd/include/healthd/healthd.h b/healthd/include/healthd/healthd.h
index 34ea55f..17efbd6 100644
--- a/healthd/include/healthd/healthd.h
+++ b/healthd/include/healthd/healthd.h
@@ -73,9 +73,14 @@
bool (*screen_on)(android::BatteryProperties *props);
};
+enum EventWakeup {
+ EVENT_NO_WAKEUP_FD,
+ EVENT_WAKEUP_FD,
+};
+
// Global helper functions
-int healthd_register_event(int fd, void (*handler)(uint32_t));
+int healthd_register_event(int fd, void (*handler)(uint32_t), EventWakeup wakeup = EVENT_NO_WAKEUP_FD);
void healthd_battery_update();
android::status_t healthd_get_property(int id,
struct android::BatteryProperty *val);
diff --git a/include/system/qemu_pipe.h b/include/system/qemu_pipe.h
new file mode 100644
index 0000000..af25079
--- /dev/null
+++ b/include/system/qemu_pipe.h
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2011 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 ANDROID_INCLUDE_SYSTEM_QEMU_PIPE_H
+#define ANDROID_INCLUDE_SYSTEM_QEMU_PIPE_H
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+
+// Define QEMU_PIPE_DEBUG if you want to print error messages when an error
+// occurs during pipe operations. The macro should simply take a printf-style
+// formatting string followed by optional arguments.
+#ifndef QEMU_PIPE_DEBUG
+# define QEMU_PIPE_DEBUG(...) (void)0
+#endif
+
+// Try to open a new Qemu fast-pipe. This function returns a file descriptor
+// that can be used to communicate with a named service managed by the
+// emulator.
+//
+// This file descriptor can be used as a standard pipe/socket descriptor.
+//
+// 'pipeName' is the name of the emulator service you want to connect to,
+// and must begin with 'pipe:' (e.g. 'pipe:camera' or 'pipe:opengles').
+//
+// On success, return a valid file descriptor, or -1/errno on failure. E.g.:
+//
+// EINVAL -> unknown/unsupported pipeName
+// ENOSYS -> fast pipes not available in this system.
+//
+// ENOSYS should never happen, except if you're trying to run within a
+// misconfigured emulator.
+//
+// You should be able to open several pipes to the same pipe service,
+// except for a few special cases (e.g. GSM modem), where EBUSY will be
+// returned if more than one client tries to connect to it.
+static __inline__ int qemu_pipe_open(const char* pipeName) {
+ // Sanity check.
+ if (!pipeName || memcmp(pipeName, "pipe:", 5) != 0) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ int fd = TEMP_FAILURE_RETRY(open("/dev/qemu_pipe", O_RDWR));
+ if (fd < 0) {
+ QEMU_PIPE_DEBUG("%s: Could not open /dev/qemu_pipe: %s", __FUNCTION__,
+ strerror(errno));
+ return -1;
+ }
+
+ // Write the pipe name, *including* the trailing zero which is necessary.
+ size_t pipeNameLen = strlen(pipeName);
+ ssize_t ret = TEMP_FAILURE_RETRY(write(fd, pipeName, pipeNameLen + 1U));
+ if (ret != (ssize_t)pipeNameLen + 1) {
+ QEMU_PIPE_DEBUG("%s: Could not connect to %s pipe service: %s",
+ __FUNCTION__, pipeName, strerror(errno));
+ if (ret == 0) {
+ errno = ECONNRESET;
+ } else if (ret > 0) {
+ errno = EINVAL;
+ }
+ return -1;
+ }
+ return fd;
+}
+
+// Send a framed message |buff| of |len| bytes through the |fd| descriptor.
+// This really adds a 4-hexchar prefix describing the payload size.
+// Returns 0 on success, and -1 on error.
+static int __inline__ qemu_pipe_frame_send(int fd,
+ const void* buff,
+ size_t len) {
+ char header[5];
+ snprintf(header, sizeof(header), "%04zx", len);
+ ssize_t ret = TEMP_FAILURE_RETRY(write(fd, header, 4));
+ if (ret != 4) {
+ QEMU_PIPE_DEBUG("Can't write qemud frame header: %s", strerror(errno));
+ return -1;
+ }
+ ret = TEMP_FAILURE_RETRY(write(fd, buff, len));
+ if (ret != (ssize_t)len) {
+ QEMU_PIPE_DEBUG("Can't write qemud frame payload: %s", strerror(errno));
+ return -1;
+ }
+ return 0;
+}
+
+// Read a frame message from |fd|, and store it into |buff| of |len| bytes.
+// If the framed message is larger than |len|, then this returns -1 and the
+// content is lost. Otherwise, this returns the size of the message. NOTE:
+// empty messages are possible in a framed wire protocol and do not mean
+// end-of-stream.
+static int __inline__ qemu_pipe_frame_recv(int fd, void* buff, size_t len) {
+ char header[5];
+ ssize_t ret = TEMP_FAILURE_RETRY(read(fd, header, 4));
+ if (ret != 4) {
+ QEMU_PIPE_DEBUG("Can't read qemud frame header: %s", strerror(errno));
+ return -1;
+ }
+ header[4] = '\0';
+ size_t size;
+ if (sscanf(header, "%04zx", &size) != 1) {
+ QEMU_PIPE_DEBUG("Malformed qemud frame header: [%.*s]", 4, header);
+ return -1;
+ }
+ if (size > len) {
+ QEMU_PIPE_DEBUG("Oversized qemud frame (% bytes, expected <= %)", size,
+ len);
+ return -1;
+ }
+ ret = TEMP_FAILURE_RETRY(read(fd, buff, size));
+ if (ret != (ssize_t)size) {
+ QEMU_PIPE_DEBUG("Could not read qemud frame payload: %s",
+ strerror(errno));
+ return -1;
+ }
+ return size;
+}
+
+#endif /* ANDROID_INCLUDE_HARDWARE_QEMUD_PIPE_H */
diff --git a/init/Android.mk b/init/Android.mk
index c82a19e..730ffc4 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -16,6 +16,14 @@
-DREBOOT_BOOTLOADER_ON_PANIC=0
endif
+ifneq (,$(filter eng,$(TARGET_BUILD_VARIANT)))
+init_options += \
+ -DSHUTDOWN_ZERO_TIMEOUT=1
+else
+init_options += \
+ -DSHUTDOWN_ZERO_TIMEOUT=0
+endif
+
init_options += -DLOG_UEVENTS=0
init_cflags += \
@@ -54,6 +62,7 @@
action.cpp \
capabilities.cpp \
descriptors.cpp \
+ devices.cpp \
import_parser.cpp \
init_parser.cpp \
log.cpp \
@@ -73,7 +82,6 @@
LOCAL_SRC_FILES:= \
bootchart.cpp \
builtins.cpp \
- devices.cpp \
init.cpp \
keychords.cpp \
property_service.cpp \
@@ -115,34 +123,6 @@
libnl \
libavb
-# Include SELinux policy. We do this here because different modules
-# need to be included based on the value of PRODUCT_FULL_TREBLE. This
-# type of conditional inclusion cannot be done in top-level files such
-# as build/target/product/embedded.mk.
-# This conditional inclusion closely mimics the conditional logic
-# inside init/init.cpp for loading SELinux policy from files.
-ifeq ($(PRODUCT_FULL_TREBLE),true)
-# Use split SELinux policy
-LOCAL_REQUIRED_MODULES += \
- mapping_sepolicy.cil \
- nonplat_sepolicy.cil \
- plat_sepolicy.cil \
- plat_sepolicy.cil.sha256 \
- secilc \
- nonplat_file_contexts \
- plat_file_contexts
-
-# Include precompiled policy, unless told otherwise
-ifneq ($(PRODUCT_PRECOMPILED_SEPOLICY),false)
-LOCAL_REQUIRED_MODULES += precompiled_sepolicy precompiled_sepolicy.plat.sha256
-endif
-
-else
-# Use monolithic SELinux policy
-LOCAL_REQUIRED_MODULES += sepolicy \
- file_contexts.bin
-endif
-
# Create symlinks.
LOCAL_POST_INSTALL_CMD := $(hide) mkdir -p $(TARGET_ROOT_OUT)/sbin; \
ln -sf ../init $(TARGET_ROOT_OUT)/sbin/ueventd; \
@@ -158,6 +138,7 @@
include $(CLEAR_VARS)
LOCAL_MODULE := init_tests
LOCAL_SRC_FILES := \
+ devices_test.cpp \
init_parser_test.cpp \
property_service_test.cpp \
util_test.cpp \
diff --git a/init/README.md b/init/README.md
index 99522b9..fc50730 100644
--- a/init/README.md
+++ b/init/README.md
@@ -192,16 +192,24 @@
`oneshot`
> Do not restart the service when it exits.
-`class <name>`
-> Specify a class name for the service. All services in a
+`class <name> [ <name>\* ]`
+> Specify class names for the service. All services in a
named class may be started or stopped together. A service
is in the class "default" if one is not specified via the
- class option.
+ class option. Additional classnames beyond the (required) first
+ one are used to group services.
+`animation class`
+> 'animation' class should include all services necessary for both
+ boot animation and shutdown animation. As these services can be
+ launched very early during bootup and can run until the last stage
+ of shutdown, access to /data partition is not guaranteed. These
+ services can check files under /data but it should not keep files opened
+ and should work when /data is not available.
`onrestart`
> Execute a Command (see below) when service restarts.
-`writepid <file...>`
+`writepid <file> [ <file>\* ]`
> Write the child's pid to the given files when it forks. Meant for
cgroup/cpuset usage. If no files under /dev/cpuset/ are specified, but the
system property 'ro.cpuset.default' is set to a non-empty cpuset name (e.g.
@@ -279,9 +287,17 @@
currently running, without disabling them. They can be restarted
later using `class_start`.
+`class_restart <serviceclass>`
+> Restarts all services of the specified class.
+
`copy <src> <dst>`
> Copies a file. Similar to write, but useful for binary/large
amounts of data.
+ Regarding to the src file, copying from symbolic link file and world-writable
+ or group-writable files are not allowed.
+ Regarding to the dst file, the default mode created is 0600 if it does not
+ exist. And it will be truncated if dst file is a normal regular file and
+ already exists.
`domainname <name>`
> Set the domain name.
@@ -302,6 +318,12 @@
groups can be provided. No other commands will be run until this one
finishes. _seclabel_ can be a - to denote default. Properties are expanded
within _argument_.
+ Init halts executing commands until the forked process exits.
+
+`exec_start <service>`
+> Start service a given service and halt processing of additional init commands
+ until it returns. It functions similarly to the `exec` command, but uses an
+ existing service definition instead of providing them as arguments.
`export <name> <value>`
> Set the environment variable _name_ equal to _value_ in the
@@ -353,7 +375,8 @@
"sys.powerctl" system property, used to implement rebooting.
`restart <service>`
-> Like stop, but doesn't disable the service.
+> Stops and restarts a running service, does nothing if the service is currently
+ restarting, otherwise, it just starts the service.
`restorecon <path> [ <path>\* ]`
> Restore the file named by _path_ to the security context specified
diff --git a/init/action.cpp b/init/action.cpp
index 1bba0f2..2ccf0bc 100644
--- a/init/action.cpp
+++ b/init/action.cpp
@@ -18,14 +18,14 @@
#include <errno.h>
-#include <android-base/strings.h>
+#include <android-base/properties.h>
#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
#include "builtins.h"
#include "error.h"
#include "init_parser.h"
#include "log.h"
-#include "property_service.h"
#include "util.h"
using android::base::Join;
@@ -219,9 +219,8 @@
found = true;
}
} else {
- std::string prop_val = property_get(trigger_name.c_str());
- if (prop_val.empty() || (trigger_value != "*" &&
- trigger_value != prop_val)) {
+ std::string prop_val = android::base::GetProperty(trigger_name, "");
+ if (prop_val.empty() || (trigger_value != "*" && trigger_value != prop_val)) {
return false;
}
}
diff --git a/init/bootchart.cpp b/init/bootchart.cpp
index 4a9c32e..beabea1 100644
--- a/init/bootchart.cpp
+++ b/init/bootchart.cpp
@@ -16,8 +16,6 @@
#include "bootchart.h"
-#include "property_service.h"
-
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
@@ -39,6 +37,7 @@
#include <android-base/file.h>
#include <android-base/logging.h>
+#include <android-base/properties.h>
#include <android-base/stringprintf.h>
using android::base::StringPrintf;
@@ -72,7 +71,7 @@
utsname uts;
if (uname(&uts) == -1) return;
- std::string fingerprint = property_get("ro.build.fingerprint");
+ std::string fingerprint = android::base::GetProperty("ro.build.fingerprint", "");
if (fingerprint.empty()) return;
std::string kernel_cmdline;
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 24875d5..64c00e9 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -45,16 +45,16 @@
#include <selinux/selinux.h>
#include <selinux/label.h>
-#include <fs_mgr.h>
#include <android-base/file.h>
#include <android-base/parseint.h>
-#include <android-base/strings.h>
+#include <android-base/properties.h>
#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
#include <bootloader_message/bootloader_message.h>
-#include <cutils/partition_utils.h>
#include <cutils/android_reboot.h>
#include <ext4_utils/ext4_crypt.h>
#include <ext4_utils/ext4_crypt_init_extensions.h>
+#include <fs_mgr.h>
#include <logwrap/logwrap.h>
#include "action.h"
@@ -148,8 +148,14 @@
return 0;
}
+static int do_class_restart(const std::vector<std::string>& args) {
+ ServiceManager::GetInstance().
+ ForEachServiceInClass(args[1], [] (Service* s) { s->Restart(); });
+ return 0;
+}
+
static int do_domainname(const std::vector<std::string>& args) {
- return write_file("/proc/sys/kernel/domainname", args[1].c_str()) ? 0 : 1;
+ return write_file("/proc/sys/kernel/domainname", args[1]) ? 0 : 1;
}
static int do_enable(const std::vector<std::string>& args) {
@@ -161,19 +167,11 @@
}
static int do_exec(const std::vector<std::string>& args) {
- Service* svc = ServiceManager::GetInstance().MakeExecOneshotService(args);
- if (!svc) {
- return -1;
- }
- if (!start_waiting_for_exec()) {
- return -1;
- }
- if (!svc->Start()) {
- stop_waiting_for_exec();
- ServiceManager::GetInstance().RemoveService(*svc);
- return -1;
- }
- return 0;
+ return ServiceManager::GetInstance().Exec(args) ? 0 : -1;
+}
+
+static int do_exec_start(const std::vector<std::string>& args) {
+ return ServiceManager::GetInstance().ExecStart(args[1]) ? 0 : -1;
}
static int do_export(const std::vector<std::string>& args) {
@@ -181,7 +179,7 @@
}
static int do_hostname(const std::vector<std::string>& args) {
- return write_file("/proc/sys/kernel/hostname", args[1].c_str()) ? 0 : 1;
+ return write_file("/proc/sys/kernel/hostname", args[1]) ? 0 : 1;
}
static int do_ifup(const std::vector<std::string>& args) {
@@ -698,67 +696,15 @@
}
static int do_write(const std::vector<std::string>& args) {
- const char* path = args[1].c_str();
- const char* value = args[2].c_str();
- return write_file(path, value) ? 0 : 1;
+ return write_file(args[1], args[2]) ? 0 : 1;
}
static int do_copy(const std::vector<std::string>& args) {
- char *buffer = NULL;
- int rc = 0;
- int fd1 = -1, fd2 = -1;
- struct stat info;
- int brtw, brtr;
- char *p;
-
- if (stat(args[1].c_str(), &info) < 0)
- return -1;
-
- if ((fd1 = open(args[1].c_str(), O_RDONLY|O_CLOEXEC)) < 0)
- goto out_err;
-
- if ((fd2 = open(args[2].c_str(), O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC, 0660)) < 0)
- goto out_err;
-
- if (!(buffer = (char*) malloc(info.st_size)))
- goto out_err;
-
- p = buffer;
- brtr = info.st_size;
- while(brtr) {
- rc = read(fd1, p, brtr);
- if (rc < 0)
- goto out_err;
- if (rc == 0)
- break;
- p += rc;
- brtr -= rc;
+ std::string data;
+ if (read_file(args[1], &data)) {
+ return write_file(args[2], data) ? 0 : 1;
}
-
- p = buffer;
- brtw = info.st_size;
- while(brtw) {
- rc = write(fd2, p, brtw);
- if (rc < 0)
- goto out_err;
- if (rc == 0)
- break;
- p += rc;
- brtw -= rc;
- }
-
- rc = 0;
- goto out;
-out_err:
- rc = -1;
-out:
- if (buffer)
- free(buffer);
- if (fd1 >= 0)
- close(fd1);
- if (fd2 >= 0)
- close(fd2);
- return rc;
+ return 1;
}
static int do_chown(const std::vector<std::string>& args) {
@@ -924,16 +870,21 @@
}
static bool is_file_crypto() {
- std::string value = property_get("ro.crypto.type");
- return value == "file";
+ return android::base::GetProperty("ro.crypto.type", "") == "file";
}
static int do_installkey(const std::vector<std::string>& args) {
if (!is_file_crypto()) {
return 0;
}
- return e4crypt_create_device_key(args[1].c_str(),
- do_installkeys_ensure_dir_exists);
+ auto unencrypted_dir = args[1] + e4crypt_unencrypted_folder;
+ if (do_installkeys_ensure_dir_exists(unencrypted_dir.c_str())) {
+ PLOG(ERROR) << "Failed to create " << unencrypted_dir;
+ return -1;
+ }
+ std::vector<std::string> exec_args = {"exec", "/system/bin/vdc", "--wait", "cryptfs",
+ "enablefilecrypto"};
+ return do_exec(exec_args);
}
static int do_init_user0(const std::vector<std::string>& args) {
@@ -942,17 +893,20 @@
BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const {
constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
+ // clang-format off
static const Map builtin_functions = {
{"bootchart", {1, 1, do_bootchart}},
{"chmod", {2, 2, do_chmod}},
{"chown", {2, 3, do_chown}},
{"class_reset", {1, 1, do_class_reset}},
+ {"class_restart", {1, 1, do_class_restart}},
{"class_start", {1, 1, do_class_start}},
{"class_stop", {1, 1, do_class_stop}},
{"copy", {2, 2, do_copy}},
{"domainname", {1, 1, do_domainname}},
{"enable", {1, 1, do_enable}},
{"exec", {1, kMax, do_exec}},
+ {"exec_start", {1, 1, do_exec_start}},
{"export", {2, 2, do_export}},
{"hostname", {1, 1, do_hostname}},
{"ifup", {1, 1, do_ifup}},
@@ -986,5 +940,6 @@
{"wait_for_prop", {2, 2, do_wait_for_prop}},
{"write", {2, 2, do_write}},
};
+ // clang-format on
return builtin_functions;
}
diff --git a/init/devices.cpp b/init/devices.cpp
index 405f92e..6b7bab9 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -286,8 +286,7 @@
}
}
-static void add_platform_device(const char *path)
-{
+void add_platform_device(const char* path) {
int path_len = strlen(path);
struct platform_node *bus;
const char *name = path;
@@ -329,8 +328,7 @@
return NULL;
}
-static void remove_platform_device(const char *path)
-{
+void remove_platform_device(const char* path) {
struct listnode *node;
struct platform_node *bus;
@@ -473,8 +471,7 @@
}
}
-static char **get_character_device_symlinks(struct uevent *uevent)
-{
+char** get_character_device_symlinks(struct uevent* uevent) {
const char *parent;
const char *slash;
char **links;
@@ -526,8 +523,24 @@
return NULL;
}
-static char **get_block_device_symlinks(struct uevent *uevent)
-{
+// replaces any unacceptable characters with '_', the
+// length of the resulting string is equal to the input string
+void sanitize_partition_name(char* s) {
+ const char* accept =
+ "abcdefghijklmnopqrstuvwxyz"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "0123456789"
+ "_-.";
+
+ if (!s) return;
+
+ while (*s) {
+ s += strspn(s, accept);
+ if (*s) *s++ = '_';
+ }
+}
+
+char** get_block_device_symlinks(struct uevent* uevent) {
const char *device;
struct platform_node *pdev;
const char *slash;
@@ -562,7 +575,7 @@
if (uevent->partition_name) {
p = strdup(uevent->partition_name);
- sanitize(p);
+ sanitize_partition_name(p);
if (strcmp(uevent->partition_name, p)) {
LOG(VERBOSE) << "Linking partition '" << uevent->partition_name << "' as '" << p << "'";
}
diff --git a/init/devices.h b/init/devices.h
index 26a064b..1654af7 100644
--- a/init/devices.h
+++ b/init/devices.h
@@ -55,4 +55,11 @@
unsigned short wildcard);
int get_device_fd();
-#endif /* _INIT_DEVICES_H */
+// Exposed for testing
+void add_platform_device(const char* path);
+void remove_platform_device(const char* path);
+char** get_character_device_symlinks(uevent* uevent);
+char** get_block_device_symlinks(struct uevent* uevent);
+void sanitize_partition_name(char* s);
+
+#endif /* _INIT_DEVICES_H */
diff --git a/init/devices_test.cpp b/init/devices_test.cpp
new file mode 100644
index 0000000..f79c96d
--- /dev/null
+++ b/init/devices_test.cpp
@@ -0,0 +1,270 @@
+/*
+ * 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 "devices.h"
+
+#include <string>
+#include <vector>
+
+#include <android-base/scopeguard.h>
+#include <gtest/gtest.h>
+
+template <char** (*Function)(uevent*)>
+void test_get_symlinks(const std::string& platform_device_name, uevent* uevent,
+ const std::vector<std::string> expected_links) {
+ add_platform_device(platform_device_name.c_str());
+ auto platform_device_remover = android::base::make_scope_guard(
+ [&platform_device_name]() { remove_platform_device(platform_device_name.c_str()); });
+
+ char** result = Function(uevent);
+ auto result_freer = android::base::make_scope_guard([result]() {
+ if (result) {
+ for (int i = 0; result[i]; i++) {
+ free(result[i]);
+ }
+ free(result);
+ }
+ });
+
+ auto expected_size = expected_links.size();
+ if (expected_size == 0) {
+ ASSERT_EQ(nullptr, result);
+ } else {
+ ASSERT_NE(nullptr, result);
+ // First assert size is equal, so we don't overrun expected_links
+ unsigned int size = 0;
+ while (result[size]) ++size;
+ ASSERT_EQ(expected_size, size);
+
+ for (unsigned int i = 0; i < size; ++i) {
+ EXPECT_EQ(expected_links[i], result[i]);
+ }
+ }
+}
+
+TEST(devices, get_character_device_symlinks_success) {
+ const char* platform_device = "/devices/platform/some_device_name";
+ uevent uevent = {
+ .path = "/devices/platform/some_device_name/usb/usb_device/name/tty2-1:1.0",
+ .subsystem = "tty",
+ };
+ std::vector<std::string> expected_result{"/dev/usb/ttyname"};
+
+ test_get_symlinks<get_character_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, get_character_device_symlinks_no_pdev_match) {
+ const char* platform_device = "/devices/platform/some_device_name";
+ uevent uevent = {
+ .path = "/device/name/tty2-1:1.0", .subsystem = "tty",
+ };
+ std::vector<std::string> expected_result;
+
+ test_get_symlinks<get_character_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, get_character_device_symlinks_nothing_after_platform_device) {
+ const char* platform_device = "/devices/platform/some_device_name";
+ uevent uevent = {
+ .path = "/devices/platform/some_device_name", .subsystem = "tty",
+ };
+ std::vector<std::string> expected_result;
+
+ test_get_symlinks<get_character_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, get_character_device_symlinks_no_usb_found) {
+ const char* platform_device = "/devices/platform/some_device_name";
+ uevent uevent = {
+ .path = "/devices/platform/some_device_name/bad/bad/", .subsystem = "tty",
+ };
+ std::vector<std::string> expected_result;
+
+ test_get_symlinks<get_character_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, get_character_device_symlinks_no_roothub) {
+ const char* platform_device = "/devices/platform/some_device_name";
+ uevent uevent = {
+ .path = "/devices/platform/some_device_name/usb/", .subsystem = "tty",
+ };
+ std::vector<std::string> expected_result;
+
+ test_get_symlinks<get_character_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, get_character_device_symlinks_no_usb_device) {
+ const char* platform_device = "/devices/platform/some_device_name";
+ uevent uevent = {
+ .path = "/devices/platform/some_device_name/usb/usb_device/", .subsystem = "tty",
+ };
+ std::vector<std::string> expected_result;
+
+ test_get_symlinks<get_character_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, get_character_device_symlinks_no_final_slash) {
+ const char* platform_device = "/devices/platform/some_device_name";
+ uevent uevent = {
+ .path = "/devices/platform/some_device_name/usb/usb_device/name", .subsystem = "tty",
+ };
+ std::vector<std::string> expected_result;
+
+ test_get_symlinks<get_character_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, get_character_device_symlinks_no_final_name) {
+ const char* platform_device = "/devices/platform/some_device_name";
+ uevent uevent = {
+ .path = "/devices/platform/some_device_name/usb/usb_device//", .subsystem = "tty",
+ };
+ std::vector<std::string> expected_result;
+
+ test_get_symlinks<get_character_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, get_block_device_symlinks_success_platform) {
+ // These are actual paths from bullhead
+ const char* platform_device = "/devices/soc.0/f9824900.sdhci";
+ uevent uevent = {
+ .path = "/devices/soc.0/f9824900.sdhci/mmc_host/mmc0/mmc0:0001/block/mmcblk0",
+ .partition_name = nullptr,
+ .partition_num = -1,
+ };
+ std::vector<std::string> expected_result{"/dev/block/platform/soc.0/f9824900.sdhci/mmcblk0"};
+
+ test_get_symlinks<get_block_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, get_block_device_symlinks_success_platform_with_partition) {
+ // These are actual paths from bullhead
+ const char* platform_device = "/devices/soc.0/f9824900.sdhci";
+ uevent uevent = {
+ .path = "/devices/soc.0/f9824900.sdhci/mmc_host/mmc0/mmc0:0001/block/mmcblk0p1",
+ .partition_name = "modem",
+ .partition_num = 1,
+ };
+ std::vector<std::string> expected_result{
+ "/dev/block/platform/soc.0/f9824900.sdhci/by-name/modem",
+ "/dev/block/platform/soc.0/f9824900.sdhci/by-num/p1",
+ "/dev/block/platform/soc.0/f9824900.sdhci/mmcblk0p1",
+ };
+
+ test_get_symlinks<get_block_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, get_block_device_symlinks_success_platform_with_partition_only_num) {
+ const char* platform_device = "/devices/soc.0/f9824900.sdhci";
+ uevent uevent = {
+ .path = "/devices/soc.0/f9824900.sdhci/mmc_host/mmc0/mmc0:0001/block/mmcblk0p1",
+ .partition_name = nullptr,
+ .partition_num = 1,
+ };
+ std::vector<std::string> expected_result{
+ "/dev/block/platform/soc.0/f9824900.sdhci/by-num/p1",
+ "/dev/block/platform/soc.0/f9824900.sdhci/mmcblk0p1",
+ };
+
+ test_get_symlinks<get_block_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, get_block_device_symlinks_success_platform_with_partition_only_name) {
+ const char* platform_device = "/devices/soc.0/f9824900.sdhci";
+ uevent uevent = {
+ .path = "/devices/soc.0/f9824900.sdhci/mmc_host/mmc0/mmc0:0001/block/mmcblk0p1",
+ .partition_name = "modem",
+ .partition_num = -1,
+ };
+ std::vector<std::string> expected_result{
+ "/dev/block/platform/soc.0/f9824900.sdhci/by-name/modem",
+ "/dev/block/platform/soc.0/f9824900.sdhci/mmcblk0p1",
+ };
+
+ test_get_symlinks<get_block_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, get_block_device_symlinks_success_pci) {
+ const char* platform_device = "/devices/do/not/match";
+ uevent uevent = {
+ .path = "/devices/pci0000:00/0000:00:1f.2/mmcblk0",
+ .partition_name = nullptr,
+ .partition_num = -1,
+ };
+ std::vector<std::string> expected_result{"/dev/block/pci/pci0000:00/0000:00:1f.2/mmcblk0"};
+
+ test_get_symlinks<get_block_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, get_block_device_symlinks_success_vbd) {
+ const char* platform_device = "/devices/do/not/match";
+ uevent uevent = {
+ .path = "/devices/vbd-1234/mmcblk0", .partition_name = nullptr, .partition_num = -1,
+ };
+ std::vector<std::string> expected_result{"/dev/block/vbd/1234/mmcblk0"};
+
+ test_get_symlinks<get_block_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, get_block_device_symlinks_no_matches) {
+ const char* platform_device = "/devices/soc.0/f9824900.sdhci";
+ uevent uevent = {
+ .path = "/devices/soc.0/not_the_device/mmc_host/mmc0/mmc0:0001/block/mmcblk0p1",
+ .partition_name = nullptr,
+ .partition_num = -1,
+ };
+ std::vector<std::string> expected_result;
+
+ test_get_symlinks<get_block_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, sanitize_null) {
+ sanitize_partition_name(nullptr);
+}
+
+TEST(devices, sanitize_empty) {
+ std::string empty;
+ sanitize_partition_name(&empty[0]);
+ EXPECT_EQ(0u, empty.size());
+}
+
+TEST(devices, sanitize_allgood) {
+ std::string good =
+ "abcdefghijklmnopqrstuvwxyz"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "0123456789"
+ "_-.";
+ std::string good_copy = good;
+ sanitize_partition_name(&good[0]);
+ EXPECT_EQ(good_copy, good);
+}
+
+TEST(devices, sanitize_somebad) {
+ std::string string = "abc!@#$%^&*()";
+ sanitize_partition_name(&string[0]);
+ EXPECT_EQ("abc__________", string);
+}
+
+TEST(devices, sanitize_allbad) {
+ std::string string = "!@#$%^&*()";
+ sanitize_partition_name(&string[0]);
+ EXPECT_EQ("__________", string);
+}
+
+TEST(devices, sanitize_onebad) {
+ std::string string = ")";
+ sanitize_partition_name(&string[0]);
+ EXPECT_EQ("_", string);
+}
diff --git a/init/init.cpp b/init/init.cpp
index 0ce1c05..e14034f 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -40,14 +40,12 @@
#include <selinux/label.h>
#include <selinux/android.h>
+#include <android-base/chrono_utils.h>
#include <android-base/file.h>
+#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
-#include <cutils/fs.h>
-#include <cutils/iosched_policy.h>
-#include <cutils/list.h>
-#include <cutils/sockets.h>
#include <libavb/libavb.h>
#include <private/android_filesystem_config.h>
@@ -72,6 +70,8 @@
#include "util.h"
#include "watchdogd.h"
+using android::base::boot_clock;
+using android::base::GetProperty;
using android::base::StringPrintf;
struct selabel_handle *sehandle;
@@ -86,8 +86,6 @@
const char *ENV[32];
-static std::unique_ptr<Timer> waiting_for_exec(nullptr);
-
static int epoll_fd = -1;
static std::unique_ptr<Timer> waiting_for_prop(nullptr);
@@ -135,29 +133,12 @@
return -1;
}
-bool start_waiting_for_exec()
-{
- if (waiting_for_exec) {
- return false;
- }
- waiting_for_exec.reset(new Timer());
- return true;
-}
-
-void stop_waiting_for_exec()
-{
- if (waiting_for_exec) {
- LOG(INFO) << "Wait for exec took " << *waiting_for_exec;
- waiting_for_exec.reset();
- }
-}
-
bool start_waiting_for_property(const char *name, const char *value)
{
if (waiting_for_prop) {
return false;
}
- if (property_get(name) != value) {
+ if (GetProperty(name, "") != value) {
// Current property value is not equal to expected value
wait_prop_name = name;
wait_prop_value = value;
@@ -445,7 +426,7 @@
static int console_init_action(const std::vector<std::string>& args)
{
- std::string console = property_get("ro.boot.console");
+ std::string console = GetProperty("ro.boot.console", "");
if (!console.empty()) {
default_console = "/dev/" + console;
}
@@ -469,11 +450,11 @@
}
static void export_oem_lock_status() {
- if (property_get("ro.oem_unlock_supported") != "1") {
+ if (!android::base::GetBoolProperty("ro.oem_unlock_supported", false)) {
return;
}
- std::string value = property_get("ro.boot.verifiedbootstate");
+ std::string value = GetProperty("ro.boot.verifiedbootstate", "");
if (!value.empty()) {
property_set("ro.boot.flash.locked", value == "orange" ? "0" : "1");
@@ -494,7 +475,7 @@
{ "ro.boot.revision", "ro.revision", "0", },
};
for (size_t i = 0; i < arraysize(prop_map); i++) {
- std::string value = property_get(prop_map[i].src_prop);
+ std::string value = GetProperty(prop_map[i].src_prop, "");
property_set(prop_map[i].dst_prop, (!value.empty()) ? value.c_str() : prop_map[i].default_value);
}
}
@@ -896,6 +877,24 @@
}
}
+// The files and directories that were created before initial sepolicy load
+// need to have their security context restored to the proper value.
+// This must happen before /dev is populated by ueventd.
+static void selinux_restore_context() {
+ LOG(INFO) << "Running restorecon...";
+ restorecon("/dev");
+ restorecon("/dev/kmsg");
+ restorecon("/dev/socket");
+ restorecon("/dev/random");
+ restorecon("/dev/urandom");
+ restorecon("/dev/__properties__");
+ restorecon("/plat_property_contexts");
+ restorecon("/nonplat_property_contexts");
+ restorecon("/sys", SELINUX_ANDROID_RESTORECON_RECURSE);
+ restorecon("/dev/block", SELINUX_ANDROID_RESTORECON_RECURSE);
+ restorecon("/dev/device-mapper");
+}
+
// Set the UDC controller for the ConfigFS USB Gadgets.
// Read the UDC controller in use from "/sys/class/udc".
// In case of multiple UDC controllers select the first one.
@@ -1234,22 +1233,7 @@
// Now set up SELinux for second stage.
selinux_initialize(false);
-
- // These directories were necessarily created before initial policy load
- // and therefore need their security context restored to the proper value.
- // This must happen before /dev is populated by ueventd.
- LOG(INFO) << "Running restorecon...";
- restorecon("/dev");
- restorecon("/dev/kmsg");
- restorecon("/dev/socket");
- restorecon("/dev/random");
- restorecon("/dev/urandom");
- restorecon("/dev/__properties__");
- restorecon("/plat_property_contexts");
- restorecon("/nonplat_property_contexts");
- restorecon("/sys", SELINUX_ANDROID_RESTORECON_RECURSE);
- restorecon("/dev/block", SELINUX_ANDROID_RESTORECON_RECURSE);
- restorecon("/dev/device-mapper");
+ selinux_restore_context();
epoll_fd = epoll_create1(EPOLL_CLOEXEC);
if (epoll_fd == -1) {
@@ -1271,7 +1255,7 @@
parser.AddSectionParser("service",std::make_unique<ServiceParser>());
parser.AddSectionParser("on", std::make_unique<ActionParser>());
parser.AddSectionParser("import", std::make_unique<ImportParser>());
- std::string bootscript = property_get("ro.boot.init_rc");
+ std::string bootscript = GetProperty("ro.boot.init_rc", "");
if (bootscript.empty()) {
parser.ParseConfig("/init.rc");
parser.set_is_system_etc_init_loaded(
@@ -1311,7 +1295,7 @@
am.QueueBuiltinAction(mix_hwrng_into_linux_rng_action, "mix_hwrng_into_linux_rng");
// Don't mount filesystems or start core system services in charger mode.
- std::string bootmode = property_get("ro.bootmode");
+ std::string bootmode = GetProperty("ro.bootmode", "");
if (bootmode == "charger") {
am.QueueEventTrigger("charger");
} else {
@@ -1322,22 +1306,24 @@
am.QueueBuiltinAction(queue_property_triggers_action, "queue_property_triggers");
while (true) {
- if (!(waiting_for_exec || waiting_for_prop)) {
- am.ExecuteOneCommand();
- restart_processes();
- }
-
// By default, sleep until something happens.
int epoll_timeout_ms = -1;
- // If there's a process that needs restarting, wake up in time for that.
- if (process_needs_restart_at != 0) {
- epoll_timeout_ms = (process_needs_restart_at - time(nullptr)) * 1000;
- if (epoll_timeout_ms < 0) epoll_timeout_ms = 0;
+ if (!(waiting_for_prop || ServiceManager::GetInstance().IsWaitingForExec())) {
+ am.ExecuteOneCommand();
}
+ if (!(waiting_for_prop || ServiceManager::GetInstance().IsWaitingForExec())) {
+ restart_processes();
- // If there's more work to do, wake up again immediately.
- if (am.HasMoreCommands()) epoll_timeout_ms = 0;
+ // If there's a process that needs restarting, wake up in time for that.
+ if (process_needs_restart_at != 0) {
+ epoll_timeout_ms = (process_needs_restart_at - time(nullptr)) * 1000;
+ if (epoll_timeout_ms < 0) epoll_timeout_ms = 0;
+ }
+
+ // If there's more work to do, wake up again immediately.
+ if (am.HasMoreCommands()) epoll_timeout_ms = 0;
+ }
epoll_event ev;
int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, epoll_timeout_ms));
diff --git a/init/init.h b/init/init.h
index 3768c02..fe850ef 100644
--- a/init/init.h
+++ b/init/init.h
@@ -19,9 +19,6 @@
#include <string>
-class Action;
-class Service;
-
extern const char *ENV[32];
extern std::string default_console;
extern struct selabel_handle *sehandle;
@@ -35,10 +32,6 @@
int add_environment(const char* key, const char* val);
-bool start_waiting_for_exec();
-
-void stop_waiting_for_exec();
-
bool start_waiting_for_property(const char *name, const char *value);
#endif /* _INIT_INIT_H */
diff --git a/init/init_parser.cpp b/init/init_parser.cpp
index 326ebf2..a192862 100644
--- a/init/init_parser.cpp
+++ b/init/init_parser.cpp
@@ -96,7 +96,7 @@
LOG(INFO) << "Parsing file " << path << "...";
Timer t;
std::string data;
- if (!read_file(path.c_str(), &data)) {
+ if (!read_file(path, &data)) {
return false;
}
diff --git a/init/keychords.cpp b/init/keychords.cpp
index 3dbb2f0..5801ea8 100644
--- a/init/keychords.cpp
+++ b/init/keychords.cpp
@@ -23,9 +23,10 @@
#include <linux/keychord.h>
#include <unistd.h>
+#include <android-base/properties.h>
+
#include "init.h"
#include "log.h"
-#include "property_service.h"
#include "service.h"
static struct input_keychord *keychords = 0;
@@ -74,7 +75,7 @@
}
// Only handle keychords if adb is enabled.
- std::string adb_enabled = property_get("init.svc.adbd");
+ std::string adb_enabled = android::base::GetProperty("init.svc.adbd", "");
if (adb_enabled == "running") {
Service* svc = ServiceManager::GetInstance().FindServiceByKeychord(id);
if (svc) {
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 983e684..a4d8b5f 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -30,10 +30,6 @@
#include <memory>
#include <vector>
-#include <cutils/misc.h>
-#include <cutils/sockets.h>
-#include <cutils/multiuser.h>
-
#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
#include <sys/_system_properties.h>
@@ -118,12 +114,6 @@
return check_mac_perms(ctl_name, sctx, cr);
}
-std::string property_get(const char* name) {
- char value[PROP_VALUE_MAX] = {0};
- __system_property_get(name, value);
- return value;
-}
-
static void write_persistent_property(const char *name, const char *value)
{
char tempPath[PATH_MAX];
@@ -592,10 +582,7 @@
static void load_override_properties() {
if (ALLOW_LOCAL_PROP_OVERRIDE) {
- std::string debuggable = property_get("ro.debuggable");
- if (debuggable == "1") {
- load_properties_from_file("/data/local.prop", NULL);
- }
+ load_properties_from_file("/data/local.prop", NULL);
}
}
diff --git a/init/property_service.h b/init/property_service.h
index 5d59473..994da63 100644
--- a/init/property_service.h
+++ b/init/property_service.h
@@ -32,7 +32,6 @@
void load_persist_props(void);
void load_system_props(void);
void start_property_service(void);
-std::string property_get(const char* name);
uint32_t property_set(const std::string& name, const std::string& value);
bool is_legal_property_name(const std::string& name);
diff --git a/init/reboot.cpp b/init/reboot.cpp
index 3e2d61e..62e5c85 100644
--- a/init/reboot.cpp
+++ b/init/reboot.cpp
@@ -26,18 +26,18 @@
#include <sys/wait.h>
#include <memory>
+#include <set>
#include <string>
#include <thread>
#include <vector>
#include <android-base/file.h>
#include <android-base/macros.h>
-#include <android-base/parseint.h>
+#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <bootloader_message/bootloader_message.h>
#include <cutils/android_reboot.h>
-#include <cutils/partition_utils.h>
#include <fs_mgr.h>
#include <logwrap/logwrap.h>
@@ -182,6 +182,7 @@
static void __attribute__((noreturn))
RebootSystem(unsigned int cmd, const std::string& rebootTarget) {
+ LOG(INFO) << "Reboot ending, jumping to kernel";
switch (cmd) {
case ANDROID_RB_POWEROFF:
reboot(RB_POWER_OFF);
@@ -201,6 +202,16 @@
abort();
}
+static void DoSync() {
+ // quota sync is not done by sync call, so should be done separately.
+ // quota sync is in VFS level, so do it before sync, which goes down to fs level.
+ int r = quotactl(QCMD(Q_SYNC, 0), nullptr, 0 /* do not care */, 0 /* do not care */);
+ if (r < 0) {
+ PLOG(ERROR) << "quotactl failed";
+ }
+ sync();
+}
+
/* Find all read+write block devices and emulated devices in /proc/mounts
* and add them to correpsponding list.
*/
@@ -239,8 +250,9 @@
flags);
} else {
umountDone = false;
- PLOG(WARNING) << StringPrintf("cannot umount %s, flags:0x%x",
- entry.mnt_fsname().c_str(), flags);
+ PLOG(WARNING) << StringPrintf("cannot umount %s, mnt_dir %s, flags:0x%x",
+ entry.mnt_fsname().c_str(),
+ entry.mnt_dir().c_str(), flags);
}
}
}
@@ -252,6 +264,8 @@
return umountDone;
}
+static void KillAllProcesses() { android::base::WriteStringToFile("i", "/proc/sysrq-trigger"); }
+
/* Try umounting all emulated file systems R/W block device cfile systems.
* This will just try umount and give it up if it fails.
* For fs like ext4, this is ok as file system will be marked as unclean shutdown
@@ -261,7 +275,8 @@
*
* return true when umount was successful. false when timed out.
*/
-static UmountStat TryUmountAndFsck(bool runFsck) {
+static UmountStat TryUmountAndFsck(bool runFsck, int timeoutMs) {
+ Timer t;
std::vector<MountEntry> emulatedPartitions;
std::vector<MountEntry> blockDevRwPartitions;
@@ -279,18 +294,27 @@
UmountPartitions(&emulatedPartitions, 1, MNT_DETACH);
}
}
+ DoSync(); // emulated partition change can lead to update
UmountStat stat = UMOUNT_STAT_SUCCESS;
/* data partition needs all pending writes to be completed and all emulated partitions
* umounted. If umount failed in the above step, it DETACH is requested, so umount can
* still happen while waiting for /data. If the current waiting is not good enough, give
* up and leave it to e2fsck after reboot to fix it.
*/
- /* TODO update max waiting time based on usage data */
- if (!UmountPartitions(&blockDevRwPartitions, 100, 0)) {
- /* Last resort, detach and hope it finish before shutdown. */
- UmountPartitions(&blockDevRwPartitions, 1, MNT_DETACH);
- stat = UMOUNT_STAT_TIMEOUT;
+ int remainingTimeMs = timeoutMs - t.duration_ms();
+ // each retry takes 100ms, and run at least once.
+ int retry = std::max(remainingTimeMs / 100, 1);
+ if (!UmountPartitions(&blockDevRwPartitions, retry, 0)) {
+ /* Last resort, kill all and try again */
+ LOG(WARNING) << "umount still failing, trying kill all";
+ KillAllProcesses();
+ DoSync();
+ if (!UmountPartitions(&blockDevRwPartitions, 1, 0)) {
+ stat = UMOUNT_STAT_TIMEOUT;
+ }
}
+ // fsck part is excluded from timeout check. It only runs for user initiated shutdown
+ // and should not affect reboot time.
if (stat == UMOUNT_STAT_SUCCESS && runFsck) {
for (auto& entry : blockDevRwPartitions) {
DoFsck(entry);
@@ -300,16 +324,6 @@
return stat;
}
-static void DoSync() {
- // quota sync is not done by sync cal, so should be done separately.
- // quota sync is in VFS level, so do it before sync, which goes down to fs level.
- int r = quotactl(QCMD(Q_SYNC, 0), nullptr, 0 /* do not care */, 0 /* do not care */);
- if (r < 0) {
- PLOG(ERROR) << "quotactl failed";
- }
- sync();
-}
-
static void __attribute__((noreturn)) DoThermalOff() {
LOG(WARNING) << "Thermal system shutdown";
DoSync();
@@ -320,12 +334,7 @@
void DoReboot(unsigned int cmd, const std::string& reason, const std::string& rebootTarget,
bool runFsck) {
Timer t;
- std::string timeout = property_get("ro.build.shutdown_timeout");
- unsigned int delay = 0;
-
- if (!android::base::ParseUint(timeout, &delay)) {
- delay = 3; // force service termination by default
- }
+ LOG(INFO) << "Reboot start, reason: " << reason << ", rebootTarget: " << rebootTarget;
android::base::WriteStringToFile(StringPrintf("%s\n", reason.c_str()), LAST_REBOOT_REASON_FILE);
@@ -333,26 +342,52 @@
DoThermalOff();
abort();
}
- static const constexpr char* shutdown_critical_services[] = {"vold", "watchdogd"};
- for (const char* name : shutdown_critical_services) {
- Service* s = ServiceManager::GetInstance().FindServiceByName(name);
- if (s == nullptr) {
- LOG(WARNING) << "Shutdown critical service not found:" << name;
- continue;
- }
- s->Start(); // make sure that it is running.
- s->SetShutdownCritical();
+
+ /* TODO update default waiting time based on usage data */
+ constexpr unsigned int shutdownTimeoutDefault = 10;
+ unsigned int shutdownTimeout = shutdownTimeoutDefault;
+ if (SHUTDOWN_ZERO_TIMEOUT) { // eng build
+ shutdownTimeout = 0;
+ } else {
+ shutdownTimeout =
+ android::base::GetUintProperty("ro.build.shutdown_timeout", shutdownTimeoutDefault);
}
+ LOG(INFO) << "Shutdown timeout: " << shutdownTimeout;
+
+ // keep debugging tools until non critical ones are all gone.
+ const std::set<std::string> kill_after_apps{"tombstoned", "logd", "adbd"};
+ // watchdogd is a vendor specific component but should be alive to complete shutdown safely.
+ const std::set<std::string> to_starts{"watchdogd", "vold"};
+ ServiceManager::GetInstance().ForEachService([&kill_after_apps, &to_starts](Service* s) {
+ if (kill_after_apps.count(s->name())) {
+ s->SetShutdownCritical();
+ } else if (to_starts.count(s->name())) {
+ s->Start();
+ s->SetShutdownCritical();
+ }
+ });
+
+ Service* bootAnim = ServiceManager::GetInstance().FindServiceByName("bootanim");
+ Service* surfaceFlinger = ServiceManager::GetInstance().FindServiceByName("surfaceflinger");
+ if (bootAnim != nullptr && surfaceFlinger != nullptr && surfaceFlinger->IsRunning()) {
+ property_set("service.bootanim.exit", "0");
+ // Could be in the middle of animation. Stop and start so that it can pick
+ // up the right mode.
+ bootAnim->Stop();
+ // start all animation classes if stopped.
+ ServiceManager::GetInstance().ForEachServiceInClass("animation", [](Service* s) {
+ s->Start();
+ s->SetShutdownCritical(); // will not check animation class separately
+ });
+ bootAnim->Start();
+ surfaceFlinger->SetShutdownCritical();
+ bootAnim->SetShutdownCritical();
+ }
+
// optional shutdown step
// 1. terminate all services except shutdown critical ones. wait for delay to finish
- if (delay > 0) {
+ if (shutdownTimeout > 0) {
LOG(INFO) << "terminating init services";
- // tombstoned can write to data when other services are killed. so finish it first.
- static const constexpr char* first_to_kill[] = {"tombstoned"};
- for (const char* name : first_to_kill) {
- Service* s = ServiceManager::GetInstance().FindServiceByName(name);
- if (s != nullptr) s->Stop();
- }
// Ask all services to terminate except shutdown critical ones.
ServiceManager::GetInstance().ForEachService([](Service* s) {
@@ -360,7 +395,9 @@
});
int service_count = 0;
- while (t.duration_s() < delay) {
+ // Up to half as long as shutdownTimeout or 3 seconds, whichever is lower.
+ unsigned int terminationWaitTimeout = std::min<unsigned int>((shutdownTimeout + 1) / 2, 3);
+ while (t.duration_s() < terminationWaitTimeout) {
ServiceManager::GetInstance().ReapAnyOutstandingChildren();
service_count = 0;
@@ -389,8 +426,8 @@
// minimum safety steps before restarting
// 2. kill all services except ones that are necessary for the shutdown sequence.
- ServiceManager::GetInstance().ForEachService([](Service* s) {
- if (!s->IsShutdownCritical()) s->Stop();
+ ServiceManager::GetInstance().ForEachService([&kill_after_apps](Service* s) {
+ if (!s->IsShutdownCritical() || kill_after_apps.count(s->name())) s->Stop();
});
ServiceManager::GetInstance().ReapAnyOutstandingChildren();
@@ -398,14 +435,12 @@
Service* voldService = ServiceManager::GetInstance().FindServiceByName("vold");
if (voldService != nullptr && voldService->IsRunning()) {
ShutdownVold();
- voldService->Terminate();
} else {
LOG(INFO) << "vold not running, skipping vold shutdown";
}
-
// 4. sync, try umount, and optionally run fsck for user shutdown
DoSync();
- UmountStat stat = TryUmountAndFsck(runFsck);
+ UmountStat stat = TryUmountAndFsck(runFsck, shutdownTimeout * 1000 - t.duration_ms());
LogShutdownTime(stat, &t);
// Reboot regardless of umount status. If umount fails, fsck after reboot will fix it.
RebootSystem(cmd, rebootTarget);
diff --git a/init/service.cpp b/init/service.cpp
index 35aaa56..e89de9a 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -32,8 +32,10 @@
#include <selinux/selinux.h>
+#include <android-base/chrono_utils.h>
#include <android-base/file.h>
#include <android-base/parseint.h>
+#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <system/thread_defs.h>
@@ -47,6 +49,7 @@
#include "property_service.h"
#include "util.h"
+using android::base::boot_clock;
using android::base::ParseInt;
using android::base::StringPrintf;
using android::base::WriteStringToFile;
@@ -149,33 +152,50 @@
: name(name), value(value) {
}
-Service::Service(const std::string& name, const std::string& classname,
- const std::vector<std::string>& args)
- : name_(name), classname_(classname), flags_(0), pid_(0),
- crash_count_(0), uid_(0), gid_(0), namespace_flags_(0),
- seclabel_(""), ioprio_class_(IoSchedClass_NONE), ioprio_pri_(0),
- priority_(0), oom_score_adjust_(-1000), args_(args) {
+Service::Service(const std::string& name, const std::vector<std::string>& args)
+ : name_(name),
+ classnames_({"default"}),
+ flags_(0),
+ pid_(0),
+ crash_count_(0),
+ uid_(0),
+ gid_(0),
+ namespace_flags_(0),
+ seclabel_(""),
+ ioprio_class_(IoSchedClass_NONE),
+ ioprio_pri_(0),
+ priority_(0),
+ oom_score_adjust_(-1000),
+ args_(args) {
onrestart_.InitSingleTrigger("onrestart");
}
-Service::Service(const std::string& name, const std::string& classname,
- unsigned flags, uid_t uid, gid_t gid,
- const std::vector<gid_t>& supp_gids,
- const CapSet& capabilities, unsigned namespace_flags,
- const std::string& seclabel,
+Service::Service(const std::string& name, unsigned flags, uid_t uid, gid_t gid,
+ const std::vector<gid_t>& supp_gids, const CapSet& capabilities,
+ unsigned namespace_flags, const std::string& seclabel,
const std::vector<std::string>& args)
- : name_(name), classname_(classname), flags_(flags), pid_(0),
- crash_count_(0), uid_(uid), gid_(gid),
- supp_gids_(supp_gids), capabilities_(capabilities),
- namespace_flags_(namespace_flags), seclabel_(seclabel),
- ioprio_class_(IoSchedClass_NONE), ioprio_pri_(0), priority_(0),
- oom_score_adjust_(-1000), args_(args) {
+ : name_(name),
+ classnames_({"default"}),
+ flags_(flags),
+ pid_(0),
+ crash_count_(0),
+ uid_(uid),
+ gid_(gid),
+ supp_gids_(supp_gids),
+ capabilities_(capabilities),
+ namespace_flags_(namespace_flags),
+ seclabel_(seclabel),
+ ioprio_class_(IoSchedClass_NONE),
+ ioprio_pri_(0),
+ priority_(0),
+ oom_score_adjust_(-1000),
+ args_(args) {
onrestart_.InitSingleTrigger("onrestart");
}
void Service::NotifyStateChange(const std::string& new_state) const {
- if ((flags_ & SVC_EXEC) != 0) {
- // 'exec' commands don't have properties tracking their state.
+ if ((flags_ & SVC_TEMPORARY) != 0) {
+ // Services created by 'exec' are temporary and don't have properties tracking their state.
return;
}
@@ -193,7 +213,13 @@
LOG(INFO) << "Sending signal " << signal
<< " to service '" << name_
<< "' (pid " << pid_ << ") process group...";
- if (killProcessGroup(uid_, pid_, signal) == -1) {
+ int r;
+ if (signal == SIGTERM) {
+ r = killProcessGroupOnce(uid_, pid_, signal);
+ } else {
+ r = killProcessGroup(uid_, pid_, signal);
+ }
+ if (r == -1) {
PLOG(ERROR) << "killProcessGroup(" << uid_ << ", " << pid_ << ", " << signal << ") failed";
}
if (kill(-pid_, signal) == -1) {
@@ -242,7 +268,7 @@
}
}
-bool Service::Reap() {
+void Service::Reap() {
if (!(flags_ & SVC_ONESHOT) || (flags_ & SVC_RESTART)) {
KillProcessGroup(SIGKILL);
}
@@ -253,7 +279,10 @@
if (flags_ & SVC_EXEC) {
LOG(INFO) << "SVC_EXEC pid " << pid_ << " finished...";
- return true;
+ }
+
+ if (flags_ & SVC_TEMPORARY) {
+ return;
}
pid_ = 0;
@@ -268,7 +297,7 @@
// Disabled and reset processes do not get restarted automatically.
if (flags_ & (SVC_DISABLED | SVC_RESET)) {
NotifyStateChange("stopped");
- return false;
+ return;
}
// If we crash > 4 times in 4 minutes, reboot into recovery.
@@ -292,12 +321,12 @@
onrestart_.ExecuteAllCommands();
NotifyStateChange("restarting");
- return false;
+ return;
}
void Service::DumpState() const {
LOG(INFO) << "service " << name_;
- LOG(INFO) << " class '" << classname_ << "'";
+ LOG(INFO) << " class '" << android::base::Join(classnames_, " ") << "'";
LOG(INFO) << " exec "<< android::base::Join(args_, " ");
std::for_each(descriptors_.begin(), descriptors_.end(),
[] (const auto& info) { LOG(INFO) << *info; });
@@ -334,7 +363,7 @@
}
bool Service::ParseClass(const std::vector<std::string>& args, std::string* err) {
- classname_ = args[1];
+ classnames_ = std::set<std::string>(args.begin() + 1, args.end());
return true;
}
@@ -516,10 +545,11 @@
Service::OptionParserMap::Map& Service::OptionParserMap::map() const {
constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
+ // clang-format off
static const Map option_parsers = {
{"capabilities",
{1, kMax, &Service::ParseCapabilities}},
- {"class", {1, 1, &Service::ParseClass}},
+ {"class", {1, kMax, &Service::ParseClass}},
{"console", {0, 1, &Service::ParseConsole}},
{"critical", {0, 0, &Service::ParseCritical}},
{"disabled", {0, 0, &Service::ParseDisabled}},
@@ -539,6 +569,7 @@
{"user", {1, 1, &Service::ParseUser}},
{"writepid", {1, kMax, &Service::ParseWritepid}},
};
+ // clang-format on
return option_parsers;
}
@@ -558,6 +589,18 @@
return (this->*parser)(args, err);
}
+bool Service::ExecStart(std::unique_ptr<Timer>* exec_waiter) {
+ flags_ |= SVC_EXEC | SVC_ONESHOT;
+
+ exec_waiter->reset(new Timer);
+
+ if (!Start()) {
+ exec_waiter->reset();
+ return false;
+ }
+ return true;
+}
+
bool Service::Start() {
// Starting a service removes it from the disabled or reset state and
// immediately takes it out of the restarting state if it was in there.
@@ -638,7 +681,7 @@
if (iter == writepid_files_.end()) {
// There were no "writepid" instructions for cpusets, check if the system default
// cpuset is specified to be used for the process.
- std::string default_cpuset = property_get("ro.cpuset.default");
+ std::string default_cpuset = android::base::GetProperty("ro.cpuset.default", "");
if (!default_cpuset.empty()) {
// Make sure the cpuset name starts and ends with '/'.
// A single '/' means the 'root' cpuset.
@@ -844,6 +887,35 @@
services_.emplace_back(std::move(service));
}
+bool ServiceManager::Exec(const std::vector<std::string>& args) {
+ Service* svc = MakeExecOneshotService(args);
+ if (!svc) {
+ LOG(ERROR) << "Could not create exec service";
+ return false;
+ }
+ if (!svc->ExecStart(&exec_waiter_)) {
+ LOG(ERROR) << "Could not start exec service";
+ ServiceManager::GetInstance().RemoveService(*svc);
+ return false;
+ }
+ return true;
+}
+
+bool ServiceManager::ExecStart(const std::string& name) {
+ Service* svc = FindServiceByName(name);
+ if (!svc) {
+ LOG(ERROR) << "ExecStart(" << name << "): Service not found";
+ return false;
+ }
+ if (!svc->ExecStart(&exec_waiter_)) {
+ LOG(ERROR) << "ExecStart(" << name << "): Could not start Service";
+ return false;
+ }
+ return true;
+}
+
+bool ServiceManager::IsWaitingForExec() const { return exec_waiter_ != nullptr; }
+
Service* ServiceManager::MakeExecOneshotService(const std::vector<std::string>& args) {
// Parse the arguments: exec [SECLABEL [UID [GID]*] --] COMMAND ARGS...
// SECLABEL can be a - to denote default
@@ -867,7 +939,7 @@
exec_count_++;
std::string name = StringPrintf("exec %d (%s)", exec_count_, str_args[0].c_str());
- unsigned flags = SVC_EXEC | SVC_ONESHOT;
+ unsigned flags = SVC_EXEC | SVC_ONESHOT | SVC_TEMPORARY;
CapSet no_capabilities;
unsigned namespace_flags = 0;
@@ -889,15 +961,10 @@
}
}
- std::unique_ptr<Service> svc_p(new Service(name, "default", flags, uid, gid, supp_gids,
- no_capabilities, namespace_flags, seclabel,
- str_args));
- if (!svc_p) {
- LOG(ERROR) << "Couldn't allocate service for exec of '" << str_args[0] << "'";
- return nullptr;
- }
+ auto svc_p = std::make_unique<Service>(name, flags, uid, gid, supp_gids, no_capabilities,
+ namespace_flags, seclabel, str_args);
Service* svc = svc_p.get();
- services_.push_back(std::move(svc_p));
+ services_.emplace_back(std::move(svc_p));
return svc;
}
@@ -945,7 +1012,7 @@
void ServiceManager::ForEachServiceInClass(const std::string& classname,
void (*func)(Service* svc)) const {
for (const auto& s : services_) {
- if (classname == s->classname()) {
+ if (s->classnames().find(classname) != s->classnames().end()) {
func(s.get());
}
}
@@ -1012,8 +1079,13 @@
return true;
}
- if (svc->Reap()) {
- stop_waiting_for_exec();
+ svc->Reap();
+
+ if (svc->flags() & SVC_EXEC) {
+ LOG(INFO) << "Wait for exec took " << *exec_waiter_;
+ exec_waiter_.reset();
+ }
+ if (svc->flags() & SVC_TEMPORARY) {
RemoveService(*svc);
}
@@ -1039,7 +1111,7 @@
}
std::vector<std::string> str_args(args.begin() + 2, args.end());
- service_ = std::make_unique<Service>(name, "default", str_args);
+ service_ = std::make_unique<Service>(name, str_args);
return true;
}
diff --git a/init/service.h b/init/service.h
index 08388e2..d84ce02 100644
--- a/init/service.h
+++ b/init/service.h
@@ -22,9 +22,12 @@
#include <cutils/iosched_policy.h>
#include <memory>
+#include <set>
#include <string>
#include <vector>
+#include <android-base/chrono_utils.h>
+
#include "action.h"
#include "capabilities.h"
#include "descriptors.h"
@@ -43,10 +46,13 @@
#define SVC_RC_DISABLED 0x080 // Remember if the disabled flag was set in the rc script.
#define SVC_RESTART 0x100 // Use to safely restart (stop, wait, start) a service.
#define SVC_DISABLED_START 0x200 // A start was requested but it was disabled at the time.
-#define SVC_EXEC 0x400 // This synthetic service corresponds to an 'exec'.
+#define SVC_EXEC 0x400 // This service was started by either 'exec' or 'exec_start' and stops
+ // init from processing more commands until it completes
#define SVC_SHUTDOWN_CRITICAL 0x800 // This service is critical for shutdown and
// should not be killed during shutdown
+#define SVC_TEMPORARY 0x1000 // This service was started by 'exec' and should be removed from the
+ // service list once it is reaped.
#define NR_SVC_SUPP_GIDS 12 // twelve supplementary groups
@@ -61,18 +67,17 @@
};
class Service {
-public:
- Service(const std::string& name, const std::string& classname,
- const std::vector<std::string>& args);
+ public:
+ Service(const std::string& name, const std::vector<std::string>& args);
- Service(const std::string& name, const std::string& classname,
- unsigned flags, uid_t uid, gid_t gid,
+ Service(const std::string& name, unsigned flags, uid_t uid, gid_t gid,
const std::vector<gid_t>& supp_gids, const CapSet& capabilities,
unsigned namespace_flags, const std::string& seclabel,
const std::vector<std::string>& args);
bool IsRunning() { return (flags_ & SVC_RUNNING) != 0; }
bool ParseLine(const std::vector<std::string>& args, std::string* err);
+ bool ExecStart(std::unique_ptr<Timer>* exec_waiter);
bool Start();
bool StartIfNotDisabled();
bool Enable();
@@ -81,13 +86,13 @@
void Terminate();
void Restart();
void RestartIfNeeded(time_t* process_needs_restart_at);
- bool Reap();
+ void Reap();
void DumpState() const;
void SetShutdownCritical() { flags_ |= SVC_SHUTDOWN_CRITICAL; }
- bool IsShutdownCritical() { return (flags_ & SVC_SHUTDOWN_CRITICAL) != 0; }
+ bool IsShutdownCritical() const { return (flags_ & SVC_SHUTDOWN_CRITICAL) != 0; }
const std::string& name() const { return name_; }
- const std::string& classname() const { return classname_; }
+ const std::set<std::string>& classnames() const { return classnames_; }
unsigned flags() const { return flags_; }
pid_t pid() const { return pid_; }
uid_t uid() const { return uid_; }
@@ -100,7 +105,7 @@
void set_keychord_id(int keychord_id) { keychord_id_ = keychord_id; }
const std::vector<std::string>& args() const { return args_; }
-private:
+ private:
using OptionParser = bool (Service::*) (const std::vector<std::string>& args,
std::string* err);
class OptionParserMap;
@@ -136,13 +141,13 @@
bool AddDescriptor(const std::vector<std::string>& args, std::string* err);
std::string name_;
- std::string classname_;
+ std::set<std::string> classnames_;
std::string console_;
unsigned flags_;
pid_t pid_;
- boot_clock::time_point time_started_; // time of last start
- boot_clock::time_point time_crashed_; // first crash within inspection window
+ android::base::boot_clock::time_point time_started_; // time of last start
+ android::base::boot_clock::time_point time_crashed_; // first crash within inspection window
int crash_count_; // number of times crashed within window
uid_t uid_;
@@ -179,6 +184,9 @@
void AddService(std::unique_ptr<Service> service);
Service* MakeExecOneshotService(const std::vector<std::string>& args);
+ bool Exec(const std::vector<std::string>& args);
+ bool ExecStart(const std::string& name);
+ bool IsWaitingForExec() const;
Service* FindServiceByName(const std::string& name) const;
Service* FindServiceByPid(pid_t pid) const;
Service* FindServiceByKeychord(int keychord_id) const;
@@ -199,6 +207,8 @@
bool ReapOneProcess();
static int exec_count_; // Every service needs a unique name.
+ std::unique_ptr<Timer> exec_waiter_;
+
std::vector<std::unique_ptr<Service>> services_;
};
diff --git a/init/signal_handler.cpp b/init/signal_handler.cpp
index 1041b82..5e3acac 100644
--- a/init/signal_handler.cpp
+++ b/init/signal_handler.cpp
@@ -24,8 +24,6 @@
#include <unistd.h>
#include <android-base/stringprintf.h>
-#include <cutils/list.h>
-#include <cutils/sockets.h>
#include "action.h"
#include "init.h"
diff --git a/init/ueventd.cpp b/init/ueventd.cpp
index 915afbd..ba53e47 100644
--- a/init/ueventd.cpp
+++ b/init/ueventd.cpp
@@ -26,6 +26,7 @@
#include <sys/types.h>
+#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <selinux/selinux.h>
@@ -34,7 +35,6 @@
#include "util.h"
#include "devices.h"
#include "ueventd_parser.h"
-#include "property_service.h"
int ueventd_main(int argc, char **argv)
{
@@ -71,7 +71,7 @@
* TODO: cleanup platform ueventd.rc to remove vendor specific
* device node entries (b/34968103)
*/
- std::string hardware = property_get("ro.hardware");
+ std::string hardware = android::base::GetProperty("ro.hardware", "");
ueventd_parse_config_file(android::base::StringPrintf("/ueventd.%s.rc", hardware.c_str()).c_str());
device_init();
@@ -94,7 +94,7 @@
return 0;
}
-void set_device_permission(int nargs, char **args)
+void set_device_permission(const char* fn, int line, int nargs, char **args)
{
char *name;
char *attr = 0;
@@ -121,7 +121,7 @@
}
if (nargs != 4) {
- LOG(ERROR) << "invalid line ueventd.rc line for '" << args[0] << "'";
+ LOG(ERROR) << "invalid line (" << fn << ":" << line << ") line for '" << args[0] << "'";
return;
}
@@ -136,20 +136,20 @@
perm = strtol(args[1], &endptr, 8);
if (!endptr || *endptr != '\0') {
- LOG(ERROR) << "invalid mode '" << args[1] << "'";
+ LOG(ERROR) << "invalid mode (" << fn << ":" << line << ") '" << args[1] << "'";
return;
}
struct passwd* pwd = getpwnam(args[2]);
if (!pwd) {
- LOG(ERROR) << "invalid uid '" << args[2] << "'";
+ LOG(ERROR) << "invalid uid (" << fn << ":" << line << ") '" << args[2] << "'";
return;
}
uid = pwd->pw_uid;
struct group* grp = getgrnam(args[3]);
if (!grp) {
- LOG(ERROR) << "invalid gid '" << args[3] << "'";
+ LOG(ERROR) << "invalid gid (" << fn << ":" << line << ") '" << args[3] << "'";
return;
}
gid = grp->gr_gid;
diff --git a/init/ueventd_parser.cpp b/init/ueventd_parser.cpp
index baff58c..554c1e3 100644
--- a/init/ueventd_parser.cpp
+++ b/init/ueventd_parser.cpp
@@ -236,6 +236,6 @@
return 0;
}
-static void parse_line_device(parse_state*, int nargs, char** args) {
- set_device_permission(nargs, args);
+static void parse_line_device(parse_state* state, int nargs, char** args) {
+ set_device_permission(state->filename, state->line, nargs, args);
}
diff --git a/init/ueventd_parser.h b/init/ueventd_parser.h
index 907cc49..4d69897 100644
--- a/init/ueventd_parser.h
+++ b/init/ueventd_parser.h
@@ -22,7 +22,7 @@
#define UEVENTD_PARSER_MAXARGS 5
int ueventd_parse_config_file(const char *fn);
-void set_device_permission(int nargs, char **args);
+void set_device_permission(const char* fn, int line, int nargs, char **args);
struct ueventd_subsystem *ueventd_subsystem_find_by_name(const char *name);
#endif
diff --git a/init/util.cpp b/init/util.cpp
index b90e5b1..c1b7898 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -38,6 +38,7 @@
#include <android-base/file.h>
#include <android-base/logging.h>
+#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
@@ -48,10 +49,11 @@
#include "init.h"
#include "log.h"
-#include "property_service.h"
#include "reboot.h"
#include "util.h"
+using android::base::boot_clock;
+
static unsigned int do_decode_uid(const char *s)
{
unsigned int v;
@@ -161,10 +163,11 @@
return -1;
}
-bool read_file(const char* path, std::string* content) {
+bool read_file(const std::string& path, std::string* content) {
content->clear();
- android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path, O_RDONLY | O_NOFOLLOW | O_CLOEXEC)));
+ android::base::unique_fd fd(
+ TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_NOFOLLOW | O_CLOEXEC)));
if (fd == -1) {
return false;
}
@@ -184,9 +187,9 @@
return android::base::ReadFdToString(fd, content);
}
-bool write_file(const char* path, const char* content) {
- android::base::unique_fd fd(
- TEMP_FAILURE_RETRY(open(path, O_WRONLY | O_CREAT | O_NOFOLLOW | O_CLOEXEC, 0600)));
+bool write_file(const std::string& path, const std::string& content) {
+ android::base::unique_fd fd(TEMP_FAILURE_RETRY(
+ open(path.c_str(), O_WRONLY | O_CREAT | O_NOFOLLOW | O_TRUNC | O_CLOEXEC, 0600)));
if (fd == -1) {
PLOG(ERROR) << "write_file: Unable to open '" << path << "'";
return false;
@@ -198,13 +201,6 @@
return success;
}
-boot_clock::time_point boot_clock::now() {
- timespec ts;
- clock_gettime(CLOCK_BOOTTIME, &ts);
- return boot_clock::time_point(std::chrono::seconds(ts.tv_sec) +
- std::chrono::nanoseconds(ts.tv_nsec));
-}
-
int mkdir_recursive(const char *pathname, mode_t mode)
{
char buf[128];
@@ -239,27 +235,6 @@
return 0;
}
-/*
- * replaces any unacceptable characters with '_', the
- * length of the resulting string is equal to the input string
- */
-void sanitize(char *s)
-{
- const char* accept =
- "abcdefghijklmnopqrstuvwxyz"
- "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
- "0123456789"
- "_-.";
-
- if (!s)
- return;
-
- while (*s) {
- s += strspn(s, accept);
- if (*s) *s++ = '_';
- }
-}
-
int wait_for_file(const char* filename, std::chrono::nanoseconds timeout) {
boot_clock::time_point timeout_time = boot_clock::now() + timeout;
while (boot_clock::now() < timeout_time) {
@@ -395,7 +370,7 @@
return false;
}
- std::string prop_val = property_get(prop_name.c_str());
+ std::string prop_val = android::base::GetProperty(prop_name, "");
if (prop_val.empty()) {
if (def_val.empty()) {
LOG(ERROR) << "property '" << prop_name << "' doesn't exist while expanding '" << src << "'";
diff --git a/init/util.h b/init/util.h
index 81c64d7..38a7bdb 100644
--- a/init/util.h
+++ b/init/util.h
@@ -25,42 +25,35 @@
#include <ostream>
#include <string>
+#include <android-base/chrono_utils.h>
+
#define COLDBOOT_DONE "/dev/.coldboot_done"
+using android::base::boot_clock;
using namespace std::chrono_literals;
int create_socket(const char *name, int type, mode_t perm,
uid_t uid, gid_t gid, const char *socketcon);
-bool read_file(const char* path, std::string* content);
-bool write_file(const char* path, const char* content);
-
-// A std::chrono clock based on CLOCK_BOOTTIME.
-class boot_clock {
- public:
- typedef std::chrono::nanoseconds duration;
- typedef std::chrono::time_point<boot_clock, duration> time_point;
- static constexpr bool is_steady = true;
-
- static time_point now();
-};
+bool read_file(const std::string& path, std::string* content);
+bool write_file(const std::string& path, const std::string& content);
class Timer {
- public:
- Timer() : start_(boot_clock::now()) {
- }
+ public:
+ Timer() : start_(boot_clock::now()) {}
- double duration_s() const {
- typedef std::chrono::duration<double> double_duration;
- return std::chrono::duration_cast<double_duration>(boot_clock::now() - start_).count();
- }
+ double duration_s() const {
+ typedef std::chrono::duration<double> double_duration;
+ return std::chrono::duration_cast<double_duration>(boot_clock::now() - start_).count();
+ }
- int64_t duration_ms() const {
- return std::chrono::duration_cast<std::chrono::milliseconds>(boot_clock::now() - start_).count();
- }
+ int64_t duration_ms() const {
+ return std::chrono::duration_cast<std::chrono::milliseconds>(boot_clock::now() - start_)
+ .count();
+ }
- private:
- boot_clock::time_point start_;
+ private:
+ android::base::boot_clock::time_point start_;
};
std::ostream& operator<<(std::ostream& os, const Timer& t);
@@ -68,7 +61,6 @@
unsigned int decode_uid(const char *s);
int mkdir_recursive(const char *pathname, mode_t mode);
-void sanitize(char *p);
int wait_for_file(const char *filename, std::chrono::nanoseconds timeout);
void import_kernel_cmdline(bool in_qemu,
const std::function<void(const std::string&, const std::string&, bool)>&);
diff --git a/init/util_test.cpp b/init/util_test.cpp
index 24c75c4..0c0350a 100644
--- a/init/util_test.cpp
+++ b/init/util_test.cpp
@@ -17,7 +17,11 @@
#include "util.h"
#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <android-base/stringprintf.h>
+#include <android-base/test_utils.h>
#include <gtest/gtest.h>
TEST(util, read_file_ENOENT) {
@@ -28,6 +32,35 @@
EXPECT_EQ("", s); // s was cleared.
}
+TEST(util, read_file_group_writeable) {
+ std::string s("hello");
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ EXPECT_TRUE(write_file(tf.path, s)) << strerror(errno);
+ EXPECT_NE(-1, fchmodat(AT_FDCWD, tf.path, 0620, AT_SYMLINK_NOFOLLOW)) << strerror(errno);
+ EXPECT_FALSE(read_file(tf.path, &s)) << strerror(errno);
+ EXPECT_EQ("", s); // s was cleared.
+}
+
+TEST(util, read_file_world_writeable) {
+ std::string s("hello");
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ EXPECT_TRUE(write_file(tf.path, s.c_str())) << strerror(errno);
+ EXPECT_NE(-1, fchmodat(AT_FDCWD, tf.path, 0602, AT_SYMLINK_NOFOLLOW)) << strerror(errno);
+ EXPECT_FALSE(read_file(tf.path, &s)) << strerror(errno);
+ EXPECT_EQ("", s); // s was cleared.
+}
+
+TEST(util, read_file_symbolic_link) {
+ std::string s("hello");
+ errno = 0;
+ // lrwxrwxrwx 1 root root 13 1970-01-01 00:00 charger -> /sbin/healthd
+ EXPECT_FALSE(read_file("/charger", &s));
+ EXPECT_EQ(ELOOP, errno);
+ EXPECT_EQ("", s); // s was cleared.
+}
+
TEST(util, read_file_success) {
std::string s("hello");
EXPECT_TRUE(read_file("/proc/version", &s));
@@ -37,6 +70,51 @@
EXPECT_STREQ("Linux", s.c_str());
}
+TEST(util, write_file_binary) {
+ std::string contents("abcd");
+ contents.push_back('\0');
+ contents.push_back('\0');
+ contents.append("dcba");
+ ASSERT_EQ(10u, contents.size());
+
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ EXPECT_TRUE(write_file(tf.path, contents)) << strerror(errno);
+
+ std::string read_back_contents;
+ EXPECT_TRUE(read_file(tf.path, &read_back_contents)) << strerror(errno);
+ EXPECT_EQ(contents, read_back_contents);
+ EXPECT_EQ(10u, read_back_contents.size());
+}
+
+TEST(util, write_file_not_exist) {
+ std::string s("hello");
+ std::string s2("hello");
+ TemporaryDir test_dir;
+ std::string path = android::base::StringPrintf("%s/does-not-exist", test_dir.path);
+ EXPECT_TRUE(write_file(path, s));
+ EXPECT_TRUE(read_file(path, &s2));
+ EXPECT_EQ(s, s2);
+ struct stat sb;
+ int fd = open(path.c_str(), O_RDONLY | O_NOFOLLOW | O_CLOEXEC);
+ EXPECT_NE(-1, fd);
+ EXPECT_EQ(0, fstat(fd, &sb));
+ EXPECT_EQ((const unsigned int)(S_IRUSR | S_IWUSR), sb.st_mode & 0777);
+ EXPECT_EQ(0, unlink(path.c_str()));
+}
+
+TEST(util, write_file_exist) {
+ std::string s2("");
+ TemporaryFile tf;
+ ASSERT_TRUE(tf.fd != -1);
+ EXPECT_TRUE(write_file(tf.path, "1hello1")) << strerror(errno);
+ EXPECT_TRUE(read_file(tf.path, &s2));
+ EXPECT_STREQ("1hello1", s2.c_str());
+ EXPECT_TRUE(write_file(tf.path, "2ll2"));
+ EXPECT_TRUE(read_file(tf.path, &s2));
+ EXPECT_STREQ("2ll2", s2.c_str());
+}
+
TEST(util, decode_uid) {
EXPECT_EQ(0U, decode_uid("root"));
EXPECT_EQ(UINT_MAX, decode_uid("toot"));
diff --git a/libappfuse/Android.bp b/libappfuse/Android.bp
index f729faf..e659f79 100644
--- a/libappfuse/Android.bp
+++ b/libappfuse/Android.bp
@@ -24,6 +24,7 @@
cc_test {
name: "libappfuse_test",
+ test_suites: ["device-tests"],
defaults: ["libappfuse_defaults"],
shared_libs: ["libappfuse"],
srcs: [
diff --git a/libappfuse/AndroidTest.xml b/libappfuse/AndroidTest.xml
new file mode 100644
index 0000000..a9cd754
--- /dev/null
+++ b/libappfuse/AndroidTest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Config for libappfuse_test">
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="libappfuse_test->/data/local/tmp/libappfuse_test" />
+ </target_preparer>
+ <option name="test-suite-tag" value="apct" />
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="libappfuse_test" />
+ </test>
+</configuration>
\ No newline at end of file
diff --git a/libbacktrace/Android.bp b/libbacktrace/Android.bp
index 0e7c6f3..8f74a1a 100644
--- a/libbacktrace/Android.bp
+++ b/libbacktrace/Android.bp
@@ -114,7 +114,7 @@
none: true,
},
cflags: ["-O0"],
- srcs: ["backtrace_testlib.c"],
+ srcs: ["backtrace_testlib.cpp"],
target: {
linux: {
diff --git a/libbacktrace/Backtrace.cpp b/libbacktrace/Backtrace.cpp
index 4354f0b..3545661 100644
--- a/libbacktrace/Backtrace.cpp
+++ b/libbacktrace/Backtrace.cpp
@@ -155,7 +155,7 @@
case BACKTRACE_UNWIND_ERROR_THREAD_DOESNT_EXIST:
return "Thread doesn't exist";
case BACKTRACE_UNWIND_ERROR_THREAD_TIMEOUT:
- return "Thread has not repsonded to signal in time";
+ return "Thread has not responded to signal in time";
case BACKTRACE_UNWIND_ERROR_UNSUPPORTED_OPERATION:
return "Attempt to use an unsupported feature";
case BACKTRACE_UNWIND_ERROR_NO_CONTEXT:
diff --git a/libbacktrace/backtrace_test.cpp b/libbacktrace/backtrace_test.cpp
index 24e48cd..fb463b0 100644
--- a/libbacktrace/backtrace_test.cpp
+++ b/libbacktrace/backtrace_test.cpp
@@ -36,6 +36,7 @@
#include <algorithm>
#include <list>
#include <memory>
+#include <ostream>
#include <string>
#include <vector>
@@ -52,6 +53,7 @@
// For the THREAD_SIGNAL definition.
#include "BacktraceCurrent.h"
+#include "backtrace_testlib.h"
#include "thread_utils.h"
// Number of microseconds per milliseconds.
@@ -80,13 +82,6 @@
int32_t done;
};
-extern "C" {
-// Prototypes for functions in the test library.
-int test_level_one(int, int, int, int, void (*)(void*), void*);
-
-int test_recursive_call(int, void (*)(void*), void*);
-}
-
static uint64_t NanoTime() {
struct timespec t = { 0, 0 };
clock_gettime(CLOCK_MONOTONIC, &t);
@@ -1602,6 +1597,150 @@
munmap(device_map, DEVICE_MAP_SIZE);
}
+class ScopedSignalHandler {
+ public:
+ ScopedSignalHandler(int signal_number, void (*handler)(int)) : signal_number_(signal_number) {
+ memset(&action_, 0, sizeof(action_));
+ action_.sa_handler = handler;
+ sigaction(signal_number_, &action_, &old_action_);
+ }
+
+ ScopedSignalHandler(int signal_number, void (*action)(int, siginfo_t*, void*))
+ : signal_number_(signal_number) {
+ memset(&action_, 0, sizeof(action_));
+ action_.sa_flags = SA_SIGINFO;
+ action_.sa_sigaction = action;
+ sigaction(signal_number_, &action_, &old_action_);
+ }
+
+ ~ScopedSignalHandler() { sigaction(signal_number_, &old_action_, nullptr); }
+
+ private:
+ struct sigaction action_;
+ struct sigaction old_action_;
+ const int signal_number_;
+};
+
+static void SetValueAndLoop(void* data) {
+ volatile int* value = reinterpret_cast<volatile int*>(data);
+
+ *value = 1;
+ for (volatile int i = 0;; i++)
+ ;
+}
+
+static void UnwindThroughSignal(bool use_action) {
+ volatile int value = 0;
+ pid_t pid;
+ if ((pid = fork()) == 0) {
+ if (use_action) {
+ ScopedSignalHandler ssh(SIGUSR1, test_signal_action);
+
+ test_level_one(1, 2, 3, 4, SetValueAndLoop, const_cast<int*>(&value));
+ } else {
+ ScopedSignalHandler ssh(SIGUSR1, test_signal_handler);
+
+ test_level_one(1, 2, 3, 4, SetValueAndLoop, const_cast<int*>(&value));
+ }
+ }
+ ASSERT_NE(-1, pid);
+
+ int read_value = 0;
+ uint64_t start = NanoTime();
+ while (read_value == 0) {
+ usleep(1000);
+
+ // Loop until the remote function gets into the final function.
+ ASSERT_TRUE(ptrace(PTRACE_ATTACH, pid, 0, 0) == 0);
+
+ WaitForStop(pid);
+
+ std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, pid));
+
+ size_t bytes_read = backtrace->Read(reinterpret_cast<uintptr_t>(const_cast<int*>(&value)),
+ reinterpret_cast<uint8_t*>(&read_value), sizeof(read_value));
+ ASSERT_EQ(sizeof(read_value), bytes_read);
+
+ ASSERT_TRUE(ptrace(PTRACE_DETACH, pid, 0, 0) == 0);
+
+ ASSERT_TRUE(NanoTime() - start < 5 * NS_PER_SEC)
+ << "Remote process did not execute far enough in 5 seconds.";
+ }
+
+ // Now need to send a signal to the remote process.
+ kill(pid, SIGUSR1);
+
+ // Wait for the process to get to the signal handler loop.
+ Backtrace::const_iterator frame_iter;
+ start = NanoTime();
+ std::unique_ptr<Backtrace> backtrace;
+ while (true) {
+ usleep(1000);
+
+ ASSERT_TRUE(ptrace(PTRACE_ATTACH, pid, 0, 0) == 0);
+
+ WaitForStop(pid);
+
+ backtrace.reset(Backtrace::Create(pid, pid));
+ ASSERT_TRUE(backtrace->Unwind(0));
+ bool found = false;
+ for (frame_iter = backtrace->begin(); frame_iter != backtrace->end(); ++frame_iter) {
+ if (frame_iter->func_name == "test_loop_forever") {
+ ++frame_iter;
+ found = true;
+ break;
+ }
+ }
+ if (found) {
+ break;
+ }
+
+ ASSERT_TRUE(ptrace(PTRACE_DETACH, pid, 0, 0) == 0);
+
+ ASSERT_TRUE(NanoTime() - start < 5 * NS_PER_SEC)
+ << "Remote process did not get in signal handler in 5 seconds." << std::endl
+ << DumpFrames(backtrace.get());
+ }
+
+ std::vector<std::string> names;
+ // Loop through the frames, and save the function names.
+ size_t frame = 0;
+ for (; frame_iter != backtrace->end(); ++frame_iter) {
+ if (frame_iter->func_name == "test_level_four") {
+ frame = names.size() + 1;
+ }
+ names.push_back(frame_iter->func_name);
+ }
+ ASSERT_NE(0U, frame) << "Unable to find test_level_four in backtrace" << std::endl
+ << DumpFrames(backtrace.get());
+
+ // The expected order of the frames:
+ // test_loop_forever
+ // test_signal_handler|test_signal_action
+ // <OPTIONAL_FRAME> May or may not exist.
+ // SetValueAndLoop (but the function name might be empty)
+ // test_level_four
+ // test_level_three
+ // test_level_two
+ // test_level_one
+ ASSERT_LE(frame + 2, names.size()) << DumpFrames(backtrace.get());
+ ASSERT_LE(2U, frame) << DumpFrames(backtrace.get());
+ if (use_action) {
+ ASSERT_EQ("test_signal_action", names[0]) << DumpFrames(backtrace.get());
+ } else {
+ ASSERT_EQ("test_signal_handler", names[0]) << DumpFrames(backtrace.get());
+ }
+ ASSERT_EQ("test_level_three", names[frame]) << DumpFrames(backtrace.get());
+ ASSERT_EQ("test_level_two", names[frame + 1]) << DumpFrames(backtrace.get());
+ ASSERT_EQ("test_level_one", names[frame + 2]) << DumpFrames(backtrace.get());
+
+ FinishRemoteProcess(pid);
+}
+
+TEST(libbacktrace, unwind_remote_through_signal_using_handler) { UnwindThroughSignal(false); }
+
+TEST(libbacktrace, unwind_remote_through_signal_using_action) { UnwindThroughSignal(true); }
+
#if defined(ENABLE_PSS_TESTS)
#include "GetPss.h"
diff --git a/libbacktrace/backtrace_testlib.c b/libbacktrace/backtrace_testlib.cpp
similarity index 61%
rename from libbacktrace/backtrace_testlib.c
rename to libbacktrace/backtrace_testlib.cpp
index 6f6b535..3081d64 100644
--- a/libbacktrace/backtrace_testlib.c
+++ b/libbacktrace/backtrace_testlib.cpp
@@ -15,32 +15,42 @@
*/
#include <libunwind.h>
+#include <signal.h>
#include <stdio.h>
-int test_level_four(int one, int two, int three, int four,
- void (*callback_func)(void*), void* data) {
+#include "backtrace_testlib.h"
+
+void test_loop_forever() {
+ while (1)
+ ;
+}
+
+void test_signal_handler(int) { test_loop_forever(); }
+
+void test_signal_action(int, siginfo_t*, void*) { test_loop_forever(); }
+
+int test_level_four(int one, int two, int three, int four, void (*callback_func)(void*),
+ void* data) {
if (callback_func != NULL) {
callback_func(data);
} else {
- while (1) {
- }
+ while (1)
+ ;
}
return one + two + three + four;
}
-int test_level_three(int one, int two, int three, int four,
- void (*callback_func)(void*), void* data) {
- return test_level_four(one+3, two+6, three+9, four+12, callback_func, data) + 3;
+int test_level_three(int one, int two, int three, int four, void (*callback_func)(void*),
+ void* data) {
+ return test_level_four(one + 3, two + 6, three + 9, four + 12, callback_func, data) + 3;
}
-int test_level_two(int one, int two, int three, int four,
- void (*callback_func)(void*), void* data) {
- return test_level_three(one+2, two+4, three+6, four+8, callback_func, data) + 2;
+int test_level_two(int one, int two, int three, int four, void (*callback_func)(void*), void* data) {
+ return test_level_three(one + 2, two + 4, three + 6, four + 8, callback_func, data) + 2;
}
-int test_level_one(int one, int two, int three, int four,
- void (*callback_func)(void*), void* data) {
- return test_level_two(one+1, two+2, three+3, four+4, callback_func, data) + 1;
+int test_level_one(int one, int two, int three, int four, void (*callback_func)(void*), void* data) {
+ return test_level_two(one + 1, two + 2, three + 3, four + 4, callback_func, data) + 1;
}
int test_recursive_call(int level, void (*callback_func)(void*), void* data) {
diff --git a/libbacktrace/backtrace_testlib.h b/libbacktrace/backtrace_testlib.h
new file mode 100644
index 0000000..16fedc4
--- /dev/null
+++ b/libbacktrace/backtrace_testlib.h
@@ -0,0 +1,38 @@
+/*
+ * 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 _LIBBACKTRACE_BACKTRACE_TESTLIB_H
+#define _LIBBACKTRACE_BACKTRACE_TESTLIB_H
+
+#include <sys/cdefs.h>
+
+#include <libunwind.h>
+
+__BEGIN_DECLS
+
+void test_loop_forever();
+void test_signal_handler(int);
+void test_signal_action(int, siginfo_t*, void*);
+int test_level_four(int, int, int, int, void (*)(void*), void*);
+int test_level_three(int, int, int, int, void (*)(void*), void*);
+int test_level_two(int, int, int, int, void (*)(void*), void*);
+int test_level_one(int, int, int, int, void (*)(void*), void*);
+int test_recursive_call(int, void (*)(void*), void*);
+void test_get_context_and_wait(unw_context_t*, volatile int*);
+
+__END_DECLS
+
+#endif // _LIBBACKTRACE_BACKTRACE_TESTLIB_H
diff --git a/libcutils/canned_fs_config.c b/libcutils/canned_fs_config.c
index e0e6a34..96ca566 100644
--- a/libcutils/canned_fs_config.c
+++ b/libcutils/canned_fs_config.c
@@ -17,6 +17,7 @@
#include <errno.h>
#include <inttypes.h>
#include <limits.h>
+#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -41,7 +42,7 @@
}
int load_canned_fs_config(const char* fn) {
- char line[PATH_MAX + 200];
+ char buf[PATH_MAX + 200];
FILE* f;
f = fopen(fn, "r");
@@ -50,17 +51,21 @@
return -1;
}
- while (fgets(line, sizeof(line), f)) {
+ while (fgets(buf, sizeof(buf), f)) {
Path* p;
char* token;
+ char* line = buf;
+ bool rootdir;
while (canned_used >= canned_alloc) {
canned_alloc = (canned_alloc+1) * 2;
canned_data = (Path*) realloc(canned_data, canned_alloc * sizeof(Path));
}
p = canned_data + canned_used;
- p->path = strdup(strtok(line, " "));
- p->uid = atoi(strtok(NULL, " "));
+ if (line[0] == '/') line++;
+ rootdir = line[0] == ' ';
+ p->path = strdup(rootdir ? "" : strtok(line, " "));
+ p->uid = atoi(strtok(rootdir ? line : NULL, " "));
p->gid = atoi(strtok(NULL, " "));
p->mode = strtol(strtok(NULL, " "), NULL, 8); // mode is in octal
p->capabilities = 0;
diff --git a/libcutils/fs_config.c b/libcutils/fs_config.c
index f99519a..b26827c 100644
--- a/libcutils/fs_config.c
+++ b/libcutils/fs_config.c
@@ -35,22 +35,13 @@
#include <log/log.h>
#include <private/android_filesystem_config.h>
+#include <private/fs_config.h>
#include <utils/Compat.h>
#ifndef O_BINARY
#define O_BINARY 0
#endif
-/* The following structure is stored little endian */
-struct fs_path_config_from_file {
- uint16_t len;
- uint16_t mode;
- uint16_t uid;
- uint16_t gid;
- uint64_t capabilities;
- char prefix[];
-} __attribute__((__aligned__(sizeof(uint64_t))));
-
/* My kingdom for <endian.h> */
static inline uint16_t get2LE(const uint8_t* src) { return src[0] | (src[1] << 8); }
@@ -116,16 +107,21 @@
* although the developer is advised to restrict the scope to the /vendor or
* oem/ file-system since the intent is to provide support for customized
* portions of a separate vendor.img or oem.img. Has to remain open so that
- * customization can also land on /system/vendor or /system/orm. We expect
- * build-time checking or filtering when constructing the associated
- * fs_config_* files.
+ * customization can also land on /system/vendor, /system/oem or /system/odm.
+ * We expect build-time checking or filtering when constructing the associated
+ * fs_config_* files (see build/tools/fs_config/fs_config_generate.c)
*/
static const char ven_conf_dir[] = "/vendor/etc/fs_config_dirs";
static const char ven_conf_file[] = "/vendor/etc/fs_config_files";
static const char oem_conf_dir[] = "/oem/etc/fs_config_dirs";
static const char oem_conf_file[] = "/oem/etc/fs_config_files";
+static const char odm_conf_dir[] = "/odm/etc/fs_config_dirs";
+static const char odm_conf_file[] = "/odm/etc/fs_config_files";
static const char* conf[][2] = {
- {sys_conf_file, sys_conf_dir}, {ven_conf_file, ven_conf_dir}, {oem_conf_file, oem_conf_dir},
+ {sys_conf_file, sys_conf_dir},
+ {ven_conf_file, ven_conf_dir},
+ {oem_conf_file, oem_conf_dir},
+ {odm_conf_file, odm_conf_dir},
};
static const struct fs_path_config android_files[] = {
@@ -142,6 +138,8 @@
{ 00600, AID_ROOT, AID_ROOT, 0, "default.prop" },
{ 00600, AID_ROOT, AID_ROOT, 0, "odm/build.prop" },
{ 00600, AID_ROOT, AID_ROOT, 0, "odm/default.prop" },
+ { 00444, AID_ROOT, AID_ROOT, 0, odm_conf_dir + 1 },
+ { 00444, AID_ROOT, AID_ROOT, 0, odm_conf_file + 1 },
{ 00444, AID_ROOT, AID_ROOT, 0, oem_conf_dir + 1 },
{ 00444, AID_ROOT, AID_ROOT, 0, oem_conf_file + 1 },
{ 00750, AID_ROOT, AID_SHELL, 0, "sbin/fs_mgr" },
@@ -160,6 +158,7 @@
{ 00555, AID_ROOT, AID_ROOT, 0, "system/etc/ppp/*" },
{ 00555, AID_ROOT, AID_ROOT, 0, "system/etc/rc.*" },
{ 00440, AID_ROOT, AID_ROOT, 0, "system/etc/recovery.img" },
+ { 00440, AID_RADIO, AID_ROOT, 0, "system/etc/xtables.lock" },
{ 00600, AID_ROOT, AID_ROOT, 0, "vendor/build.prop" },
{ 00600, AID_ROOT, AID_ROOT, 0, "vendor/default.prop" },
{ 00444, AID_ROOT, AID_ROOT, 0, ven_conf_dir + 1 },
@@ -233,16 +232,20 @@
if (target_out_path && *target_out_path) {
/* target_out_path is the path to the directory holding content of
- * system partition but as we cannot guaranty it ends with '/system'
- * we need this below skip_len logic */
+ * system partition but as we cannot guarantee it ends with '/system'
+ * or a trailing slash or not, we need to strip them off. */
char* name = NULL;
int target_out_path_len = strlen(target_out_path);
- int skip_len = strlen("/system");
- if (target_out_path[target_out_path_len] == '/') {
- skip_len++;
+ while ((target_out_path_len > 0) &&
+ (target_out_path[target_out_path_len - strlen("/")] == '/')) {
+ --target_out_path_len;
}
- if (asprintf(&name, "%s%s", target_out_path, conf[which][dir] + skip_len) != -1) {
+ if ((target_out_path_len >= (int)strlen("/system")) &&
+ !strcmp(target_out_path + target_out_path_len - strlen("/system"), "/system")) {
+ target_out_path_len -= strlen("/system");
+ }
+ if (asprintf(&name, "%.*s%s", target_out_path_len, target_out_path, conf[which][dir]) != -1) {
fd = TEMP_FAILURE_RETRY(open(name, O_RDONLY | O_BINARY));
free(name);
}
diff --git a/libcutils/include/private/fs_config.h b/libcutils/include/private/fs_config.h
new file mode 100644
index 0000000..7dad668
--- /dev/null
+++ b/libcutils/include/private/fs_config.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _LIBS_CUTILS_PRIVATE_FS_CONFIG_H
+#define _LIBS_CUTILS_PRIVATE_FS_CONFIG_H
+
+#include <stdint.h>
+
+/*
+ * binary format for the runtime <partition>/etc/fs_config_(dirs|files)
+ * filesystem override files.
+ */
+
+/* The following structure is stored little endian */
+struct fs_path_config_from_file {
+ uint16_t len;
+ uint16_t mode;
+ uint16_t uid;
+ uint16_t gid;
+ uint64_t capabilities;
+ char prefix[];
+} __attribute__((__aligned__(sizeof(uint64_t))));
+
+#endif /* _LIBS_CUTILS_PRIVATE_FS_CONFIG_H */
diff --git a/libcutils/sched_policy.cpp b/libcutils/sched_policy.cpp
index 40144cf..73ca518 100644
--- a/libcutils/sched_policy.cpp
+++ b/libcutils/sched_policy.cpp
@@ -51,13 +51,8 @@
static pthread_once_t the_once = PTHREAD_ONCE_INIT;
-static int __sys_supports_schedgroups = -1;
static int __sys_supports_timerslack = -1;
-// File descriptors open to /dev/cpuctl/../tasks, setup by initialize, or -1 on error.
-static int bg_cgroup_fd = -1;
-static int fg_cgroup_fd = -1;
-
// File descriptors open to /dev/cpuset/../tasks, setup by initialize, or -1 on error
static int system_bg_cpuset_fd = -1;
static int bg_cpuset_fd = -1;
@@ -151,23 +146,6 @@
static void __initialize() {
const char* filename;
- if (!access("/dev/cpuctl/tasks", W_OK)) {
- __sys_supports_schedgroups = 1;
-
- filename = "/dev/cpuctl/tasks";
- fg_cgroup_fd = open(filename, O_WRONLY | O_CLOEXEC);
- if (fg_cgroup_fd < 0) {
- SLOGE("open of %s failed: %s\n", filename, strerror(errno));
- }
-
- filename = "/dev/cpuctl/bg_non_interactive/tasks";
- bg_cgroup_fd = open(filename, O_WRONLY | O_CLOEXEC);
- if (bg_cgroup_fd < 0) {
- SLOGE("open of %s failed: %s\n", filename, strerror(errno));
- }
- } else {
- __sys_supports_schedgroups = 0;
- }
if (cpusets_enabled()) {
if (!access("/dev/cpuset/tasks", W_OK)) {
@@ -276,48 +254,26 @@
}
pthread_once(&the_once, __initialize);
- if (__sys_supports_schedgroups) {
- char grpBuf[32];
+ char grpBuf[32];
- if (cpusets_enabled()) {
- if (getCGroupSubsys(tid, "cpuset", grpBuf, sizeof(grpBuf)) < 0)
- return -1;
- if (grpBuf[0] == '\0') {
- *policy = SP_FOREGROUND;
- } else if (!strcmp(grpBuf, "foreground")) {
- *policy = SP_FOREGROUND;
- } else if (!strcmp(grpBuf, "background")) {
- *policy = SP_BACKGROUND;
- } else if (!strcmp(grpBuf, "top-app")) {
- *policy = SP_TOP_APP;
- } else {
- errno = ERANGE;
- return -1;
- }
- } else {
- if (getCGroupSubsys(tid, "cpu", grpBuf, sizeof(grpBuf)) < 0)
- return -1;
- if (grpBuf[0] == '\0') {
- *policy = SP_FOREGROUND;
- } else if (!strcmp(grpBuf, "bg_non_interactive")) {
- *policy = SP_BACKGROUND;
- } else {
- errno = ERANGE;
- return -1;
- }
- }
- } else {
- int rc = sched_getscheduler(tid);
- if (rc < 0)
- return -1;
- else if (rc == SCHED_NORMAL)
+ if (cpusets_enabled()) {
+ if (getCGroupSubsys(tid, "cpuset", grpBuf, sizeof(grpBuf)) < 0) return -1;
+ if (grpBuf[0] == '\0') {
*policy = SP_FOREGROUND;
- else if (rc == SCHED_BATCH)
+ } else if (!strcmp(grpBuf, "foreground")) {
+ *policy = SP_FOREGROUND;
+ } else if (!strcmp(grpBuf, "background")) {
*policy = SP_BACKGROUND;
- else {
+ } else if (!strcmp(grpBuf, "top-app")) {
+ *policy = SP_TOP_APP;
+ } else {
errno = ERANGE;
return -1;
}
+ } else {
+ // In b/34193533, we removed bg_non_interactive cgroup, so now
+ // all threads are in FOREGROUND cgroup
+ *policy = SP_FOREGROUND;
}
return 0;
}
@@ -440,49 +396,30 @@
}
#endif
- if (__sys_supports_schedgroups) {
- int fd = -1;
+ if (schedboost_enabled()) {
int boost_fd = -1;
switch (policy) {
case SP_BACKGROUND:
- fd = bg_cgroup_fd;
boost_fd = bg_schedboost_fd;
break;
case SP_FOREGROUND:
case SP_AUDIO_APP:
case SP_AUDIO_SYS:
- fd = fg_cgroup_fd;
boost_fd = fg_schedboost_fd;
break;
case SP_TOP_APP:
- fd = fg_cgroup_fd;
boost_fd = ta_schedboost_fd;
break;
default:
- fd = -1;
boost_fd = -1;
break;
}
- if (fd > 0 && add_tid_to_cgroup(tid, fd) != 0) {
+ if (boost_fd > 0 && add_tid_to_cgroup(tid, boost_fd) != 0) {
if (errno != ESRCH && errno != ENOENT)
return -errno;
}
- if (schedboost_enabled()) {
- if (boost_fd > 0 && add_tid_to_cgroup(tid, boost_fd) != 0) {
- if (errno != ESRCH && errno != ENOENT)
- return -errno;
- }
- }
- } else {
- struct sched_param param;
-
- param.sched_priority = 0;
- sched_setscheduler(tid,
- (policy == SP_BACKGROUND) ?
- SCHED_BATCH : SCHED_NORMAL,
- ¶m);
}
if (__sys_supports_timerslack) {
diff --git a/libcutils/tests/Android.bp b/libcutils/tests/Android.bp
index 718d76b..a0b1d7b 100644
--- a/libcutils/tests/Android.bp
+++ b/libcutils/tests/Android.bp
@@ -62,6 +62,7 @@
cc_test {
name: "libcutils_test",
+ test_suites: ["device-tests"],
defaults: ["libcutils_test_default"],
host_supported: true,
shared_libs: test_libraries,
@@ -69,6 +70,7 @@
cc_test {
name: "libcutils_test_static",
+ test_suites: ["device-tests"],
defaults: ["libcutils_test_default"],
static_libs: ["libc"] + test_libraries,
stl: "libc++_static",
diff --git a/libcutils/tests/AndroidTest.xml b/libcutils/tests/AndroidTest.xml
new file mode 100644
index 0000000..dd7aca2
--- /dev/null
+++ b/libcutils/tests/AndroidTest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Config for libcutils_test">
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="libcutils_test->/data/local/tmp/libcutils_test" />
+ </target_preparer>
+ <option name="test-suite-tag" value="apct" />
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="libcutils_test" />
+ </test>
+</configuration>
\ No newline at end of file
diff --git a/liblog/event_tag_map.cpp b/liblog/event_tag_map.cpp
index bdad2c2..0b977c2 100644
--- a/liblog/event_tag_map.cpp
+++ b/liblog/event_tag_map.cpp
@@ -445,7 +445,7 @@
mmap(NULL, end[which], which ? PROT_READ : PROT_READ | PROT_WRITE,
which ? MAP_SHARED : MAP_PRIVATE, fd[which], 0);
save_errno = errno;
- close(fd[which]);
+ close(fd[which]); /* fd DONE */
fd[which] = -1;
if ((newTagMap->mapAddr[which] != MAP_FAILED) &&
(newTagMap->mapAddr[which] != NULL)) {
@@ -465,6 +465,7 @@
delete newTagMap;
return NULL;
}
+ /* See 'fd DONE' comments above and below, no need to clean up here */
}
return newTagMap;
@@ -473,7 +474,7 @@
save_errno = EINVAL;
delete newTagMap;
fail_close:
- for (which = 0; which < NUM_MAPS; ++which) close(fd[which]);
+ for (which = 0; which < NUM_MAPS; ++which) close(fd[which]); /* fd DONE */
fail_errno:
errno = save_errno;
return NULL;
diff --git a/liblog/logger_write.c b/liblog/logger_write.c
index d322c0f..84feb20 100644
--- a/liblog/logger_write.c
+++ b/liblog/logger_write.c
@@ -410,16 +410,46 @@
if (!tag) tag = "";
/* XXX: This needs to go! */
- if ((bufID != LOG_ID_RADIO) &&
- (!strcmp(tag, "HTC_RIL") ||
- !strncmp(tag, "RIL", 3) || /* Any log tag with "RIL" as the prefix */
- !strncmp(tag, "IMS", 3) || /* Any log tag with "IMS" as the prefix */
- !strcmp(tag, "AT") || !strcmp(tag, "GSM") || !strcmp(tag, "STK") ||
- !strcmp(tag, "CDMA") || !strcmp(tag, "PHONE") || !strcmp(tag, "SMS"))) {
- bufID = LOG_ID_RADIO;
- /* Inform third party apps/ril/radio.. to use Rlog or RLOG */
- snprintf(tmp_tag, sizeof(tmp_tag), "use-Rlog/RLOG-%s", tag);
- tag = tmp_tag;
+ if (bufID != LOG_ID_RADIO) {
+ switch (tag[0]) {
+ case 'H':
+ if (strcmp(tag + 1, "HTC_RIL" + 1)) break;
+ goto inform;
+ case 'R':
+ /* Any log tag with "RIL" as the prefix */
+ if (strncmp(tag + 1, "RIL" + 1, strlen("RIL") - 1)) break;
+ goto inform;
+ case 'Q':
+ /* Any log tag with "QC_RIL" as the prefix */
+ if (strncmp(tag + 1, "QC_RIL" + 1, strlen("QC_RIL") - 1)) break;
+ goto inform;
+ case 'I':
+ /* Any log tag with "IMS" as the prefix */
+ if (strncmp(tag + 1, "IMS" + 1, strlen("IMS") - 1)) break;
+ goto inform;
+ case 'A':
+ if (strcmp(tag + 1, "AT" + 1)) break;
+ goto inform;
+ case 'G':
+ if (strcmp(tag + 1, "GSM" + 1)) break;
+ goto inform;
+ case 'S':
+ if (strcmp(tag + 1, "STK" + 1) && strcmp(tag + 1, "SMS" + 1)) break;
+ goto inform;
+ case 'C':
+ if (strcmp(tag + 1, "CDMA" + 1)) break;
+ goto inform;
+ case 'P':
+ if (strcmp(tag + 1, "PHONE" + 1)) break;
+ /* FALLTHRU */
+ inform:
+ bufID = LOG_ID_RADIO;
+ snprintf(tmp_tag, sizeof(tmp_tag), "use-Rlog/RLOG-%s", tag);
+ tag = tmp_tag;
+ /* FALLTHRU */
+ default:
+ break;
+ }
}
#if __BIONIC__
diff --git a/liblog/tests/liblog_benchmark.cpp b/liblog/tests/liblog_benchmark.cpp
index 3f79552..c4bf959 100644
--- a/liblog/tests/liblog_benchmark.cpp
+++ b/liblog/tests/liblog_benchmark.cpp
@@ -527,11 +527,13 @@
/*
* 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 dozen or so syscall periods (40us)
+ * 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();
+ // log tag number 0 is not known, nor shall it ever be known
__android_log_btwrite(0, EVENT_TYPE_LONG, &i, sizeof(i));
StopBenchmarkTiming();
logd_yield();
@@ -539,6 +541,28 @@
}
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)
+ */
+static void BM_log_event_overhead_42(int iters) {
+ for (unsigned long long i = 0; i < (unsigned)iters; ++i) {
+ StartBenchmarkTiming();
+ // In system/core/logcat/event.logtags:
+ // # These are used for testing, do not modify without updating
+ // # tests/framework-tests/src/android/util/EventLogFunctionalTest.java.
+ // # system/core/liblog/tests/liblog_benchmark.cpp
+ // # 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();
+ logd_yield();
+ }
+}
+BENCHMARK(BM_log_event_overhead_42);
+
static void BM_log_event_overhead_null(int iters) {
set_log_null();
BM_log_event_overhead(iters);
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index 0538c4c..70b8a28 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -1984,6 +1984,8 @@
EXPECT_EQ(0, setuid(AID_SYSTEM)); // only one that can read security buffer
+ uid = getuid();
+ gid = getgid();
pid_t pid = getpid();
ASSERT_TRUE(NULL !=
diff --git a/libnativebridge/.clang-format b/libnativebridge/.clang-format
new file mode 120000
index 0000000..fd0645f
--- /dev/null
+++ b/libnativebridge/.clang-format
@@ -0,0 +1 @@
+../.clang-format-2
\ No newline at end of file
diff --git a/libnativeloader/.clang-format b/libnativeloader/.clang-format
new file mode 120000
index 0000000..fd0645f
--- /dev/null
+++ b/libnativeloader/.clang-format
@@ -0,0 +1 @@
+../.clang-format-2
\ No newline at end of file
diff --git a/libprocessgroup/include/processgroup/processgroup.h b/libprocessgroup/include/processgroup/processgroup.h
index 11bd8cc..47f6ff3 100644
--- a/libprocessgroup/include/processgroup/processgroup.h
+++ b/libprocessgroup/include/processgroup/processgroup.h
@@ -24,6 +24,8 @@
int killProcessGroup(uid_t uid, int initialPid, int signal);
+int killProcessGroupOnce(uid_t uid, int initialPid, int signal);
+
int createProcessGroup(uid_t uid, int initialPid);
void removeAllProcessGroups(void);
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index eb66727..1572cb3 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -252,8 +252,7 @@
}
}
-static int killProcessGroupOnce(uid_t uid, int initialPid, int signal)
-{
+static int doKillProcessGroupOnce(uid_t uid, int initialPid, int signal) {
int processes = 0;
struct ctx ctx;
pid_t pid;
@@ -282,13 +281,11 @@
return processes;
}
-int killProcessGroup(uid_t uid, int initialPid, int signal)
-{
+static int killProcessGroup(uid_t uid, int initialPid, int signal, int retry) {
std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
- int retry = 40;
int processes;
- while ((processes = killProcessGroupOnce(uid, initialPid, signal)) > 0) {
+ while ((processes = doKillProcessGroupOnce(uid, initialPid, signal)) > 0) {
LOG(VERBOSE) << "killed " << processes << " processes for processgroup " << initialPid;
if (retry > 0) {
std::this_thread::sleep_for(5ms);
@@ -313,6 +310,14 @@
}
}
+int killProcessGroup(uid_t uid, int initialPid, int signal) {
+ return killProcessGroup(uid, initialPid, signal, 40 /*maxRetry*/);
+}
+
+int killProcessGroupOnce(uid_t uid, int initialPid, int signal) {
+ return killProcessGroup(uid, initialPid, signal, 0 /*maxRetry*/);
+}
+
static bool mkdirAndChown(const char *path, mode_t mode, uid_t uid, gid_t gid)
{
if (mkdir(path, mode) == -1 && errno != EEXIST) {
diff --git a/libsuspend/autosuspend_wakeup_count.c b/libsuspend/autosuspend_wakeup_count.c
index 2da204a..4dedf7f 100644
--- a/libsuspend/autosuspend_wakeup_count.c
+++ b/libsuspend/autosuspend_wakeup_count.c
@@ -24,7 +24,6 @@
#include <stddef.h>
#include <stdbool.h>
#include <string.h>
-#include <sys/param.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
@@ -36,24 +35,12 @@
#define SYS_POWER_STATE "/sys/power/state"
#define SYS_POWER_WAKEUP_COUNT "/sys/power/wakeup_count"
-#define BASE_SLEEP_TIME 100000
-
static int state_fd;
static int wakeup_count_fd;
static pthread_t suspend_thread;
static sem_t suspend_lockout;
static const char *sleep_state = "mem";
static void (*wakeup_func)(bool success) = NULL;
-static int sleep_time = BASE_SLEEP_TIME;
-
-static void update_sleep_time(bool success) {
- if (success) {
- sleep_time = BASE_SLEEP_TIME;
- return;
- }
- // double sleep time after each failure up to one minute
- sleep_time = MIN(sleep_time * 2, 60000000);
-}
static void *suspend_thread_func(void *arg __attribute__((unused)))
{
@@ -61,12 +48,10 @@
char wakeup_count[20];
int wakeup_count_len;
int ret;
- bool success = true;
+ bool success;
while (1) {
- update_sleep_time(success);
- usleep(sleep_time);
- success = false;
+ usleep(100000);
ALOGV("%s: read wakeup_count\n", __func__);
lseek(wakeup_count_fd, 0, SEEK_SET);
wakeup_count_len = TEMP_FAILURE_RETRY(read(wakeup_count_fd, wakeup_count,
@@ -90,6 +75,7 @@
continue;
}
+ success = true;
ALOGV("%s: write %*s to wakeup_count\n", __func__, wakeup_count_len, wakeup_count);
ret = TEMP_FAILURE_RETRY(write(wakeup_count_fd, wakeup_count, wakeup_count_len));
if (ret < 0) {
@@ -98,8 +84,8 @@
} else {
ALOGV("%s: write %s to %s\n", __func__, sleep_state, SYS_POWER_STATE);
ret = TEMP_FAILURE_RETRY(write(state_fd, sleep_state, strlen(sleep_state)));
- if (ret >= 0) {
- success = true;
+ if (ret < 0) {
+ success = false;
}
void (*func)(bool success) = wakeup_func;
if (func != NULL) {
diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp
index ece623b..ee646de 100644
--- a/libunwindstack/Android.bp
+++ b/libunwindstack/Android.bp
@@ -47,12 +47,15 @@
srcs: [
"ArmExidx.cpp",
+ "DwarfMemory.cpp",
+ "DwarfOp.cpp",
"Elf.cpp",
"ElfInterface.cpp",
"ElfInterfaceArm.cpp",
"Log.cpp",
"Regs.cpp",
"Memory.cpp",
+ "Symbols.cpp",
],
shared_libs: [
@@ -87,6 +90,9 @@
srcs: [
"tests/ArmExidxDecodeTest.cpp",
"tests/ArmExidxExtractTest.cpp",
+ "tests/DwarfMemoryTest.cpp",
+ "tests/DwarfOpLogTest.cpp",
+ "tests/DwarfOpTest.cpp",
"tests/ElfInterfaceArmTest.cpp",
"tests/ElfInterfaceTest.cpp",
"tests/ElfTest.cpp",
@@ -97,6 +103,7 @@
"tests/MemoryRangeTest.cpp",
"tests/MemoryRemoteTest.cpp",
"tests/RegsTest.cpp",
+ "tests/SymbolsTest.cpp",
],
cflags: [
diff --git a/libunwindstack/DwarfEncoding.h b/libunwindstack/DwarfEncoding.h
new file mode 100644
index 0000000..0ff3b8c
--- /dev/null
+++ b/libunwindstack/DwarfEncoding.h
@@ -0,0 +1,47 @@
+/*
+ * 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 _LIBUNWINDSTACK_DWARF_ENCODING_H
+#define _LIBUNWINDSTACK_DWARF_ENCODING_H
+
+#include <stdint.h>
+
+enum DwarfEncoding : uint8_t {
+ DW_EH_PE_omit = 0xff,
+
+ DW_EH_PE_absptr = 0x00,
+ DW_EH_PE_uleb128 = 0x01,
+ DW_EH_PE_udata2 = 0x02,
+ DW_EH_PE_udata4 = 0x03,
+ DW_EH_PE_udata8 = 0x04,
+ DW_EH_PE_sleb128 = 0x09,
+ DW_EH_PE_sdata2 = 0x0a,
+ DW_EH_PE_sdata4 = 0x0b,
+ DW_EH_PE_sdata8 = 0x0c,
+
+ DW_EH_PE_pcrel = 0x10,
+ DW_EH_PE_textrel = 0x20,
+ DW_EH_PE_datarel = 0x30,
+ DW_EH_PE_funcrel = 0x40,
+ DW_EH_PE_aligned = 0x50,
+
+ // The following are special values used to encode CFA and OP operands.
+ DW_EH_PE_udata1 = 0x0d,
+ DW_EH_PE_sdata1 = 0x0e,
+ DW_EH_PE_block = 0x0f,
+};
+
+#endif // _LIBUNWINDSTACK_DWARF_ENCODING_H
diff --git a/bootstat/uptime_parser.h b/libunwindstack/DwarfError.h
similarity index 61%
copy from bootstat/uptime_parser.h
copy to libunwindstack/DwarfError.h
index 756ae9b..824c307 100644
--- a/bootstat/uptime_parser.h
+++ b/libunwindstack/DwarfError.h
@@ -14,16 +14,19 @@
* limitations under the License.
*/
-#ifndef UPTIME_PARSER_H_
-#define UPTIME_PARSER_H_
+#ifndef _LIBUNWINDSTACK_DWARF_ERROR_H
+#define _LIBUNWINDSTACK_DWARF_ERROR_H
-#include <time.h>
+#include <stdint.h>
-namespace bootstat {
+enum DwarfError : uint8_t {
+ DWARF_ERROR_NONE,
+ DWARF_ERROR_MEMORY_INVALID,
+ DWARF_ERROR_ILLEGAL_VALUE,
+ DWARF_ERROR_ILLEGAL_STATE,
+ DWARF_ERROR_STACK_INDEX_NOT_VALID,
+ DWARF_ERROR_NOT_IMPLEMENTED,
+ DWARF_ERROR_TOO_MANY_ITERATIONS,
+};
-// Returns the number of seconds the system has been on since reboot.
-time_t ParseUptime();
-
-} // namespace bootstat
-
-#endif // UPTIME_PARSER_H_
\ No newline at end of file
+#endif // _LIBUNWINDSTACK_DWARF_ERROR_H
diff --git a/libunwindstack/DwarfMemory.cpp b/libunwindstack/DwarfMemory.cpp
new file mode 100644
index 0000000..11806ea
--- /dev/null
+++ b/libunwindstack/DwarfMemory.cpp
@@ -0,0 +1,248 @@
+/*
+ * 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 <assert.h>
+#include <stdint.h>
+
+#include <string>
+
+#include "DwarfEncoding.h"
+#include "DwarfMemory.h"
+#include "Memory.h"
+
+bool DwarfMemory::ReadBytes(void* dst, size_t num_bytes) {
+ if (!memory_->Read(cur_offset_, dst, num_bytes)) {
+ return false;
+ }
+ cur_offset_ += num_bytes;
+ return true;
+}
+
+template <typename SignedType>
+bool DwarfMemory::ReadSigned(uint64_t* value) {
+ SignedType signed_value;
+ if (!ReadBytes(&signed_value, sizeof(SignedType))) {
+ return false;
+ }
+ *value = static_cast<int64_t>(signed_value);
+ return true;
+}
+
+bool DwarfMemory::ReadULEB128(uint64_t* value) {
+ uint64_t cur_value = 0;
+ uint64_t shift = 0;
+ uint8_t byte;
+ do {
+ if (!ReadBytes(&byte, 1)) {
+ return false;
+ }
+ cur_value += static_cast<uint64_t>(byte & 0x7f) << shift;
+ shift += 7;
+ } while (byte & 0x80);
+ *value = cur_value;
+ return true;
+}
+
+bool DwarfMemory::ReadSLEB128(int64_t* value) {
+ uint64_t cur_value = 0;
+ uint64_t shift = 0;
+ uint8_t byte;
+ do {
+ if (!ReadBytes(&byte, 1)) {
+ return false;
+ }
+ cur_value += static_cast<uint64_t>(byte & 0x7f) << shift;
+ shift += 7;
+ } while (byte & 0x80);
+ if (byte & 0x40) {
+ // Negative value, need to sign extend.
+ cur_value |= static_cast<uint64_t>(-1) << shift;
+ }
+ *value = static_cast<int64_t>(cur_value);
+ return true;
+}
+
+template <typename AddressType>
+size_t DwarfMemory::GetEncodedSize(uint8_t encoding) {
+ switch (encoding & 0x0f) {
+ case DW_EH_PE_absptr:
+ return sizeof(AddressType);
+ case DW_EH_PE_udata1:
+ case DW_EH_PE_sdata1:
+ return 1;
+ case DW_EH_PE_udata2:
+ case DW_EH_PE_sdata2:
+ return 2;
+ case DW_EH_PE_udata4:
+ case DW_EH_PE_sdata4:
+ return 4;
+ case DW_EH_PE_udata8:
+ case DW_EH_PE_sdata8:
+ return 8;
+ case DW_EH_PE_uleb128:
+ case DW_EH_PE_sleb128:
+ default:
+ return 0;
+ }
+}
+
+bool DwarfMemory::AdjustEncodedValue(uint8_t encoding, uint64_t* value) {
+ assert((encoding & 0x0f) == 0);
+ assert(encoding != DW_EH_PE_aligned);
+
+ // Handle the encoding.
+ switch (encoding) {
+ case DW_EH_PE_absptr:
+ // Nothing to do.
+ break;
+ case DW_EH_PE_pcrel:
+ if (pc_offset_ == static_cast<uint64_t>(-1)) {
+ // Unsupported encoding.
+ return false;
+ }
+ *value += pc_offset_;
+ break;
+ case DW_EH_PE_textrel:
+ if (text_offset_ == static_cast<uint64_t>(-1)) {
+ // Unsupported encoding.
+ return false;
+ }
+ *value += text_offset_;
+ break;
+ case DW_EH_PE_datarel:
+ if (data_offset_ == static_cast<uint64_t>(-1)) {
+ // Unsupported encoding.
+ return false;
+ }
+ *value += data_offset_;
+ break;
+ case DW_EH_PE_funcrel:
+ if (func_offset_ == static_cast<uint64_t>(-1)) {
+ // Unsupported encoding.
+ return false;
+ }
+ *value += func_offset_;
+ break;
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfMemory::ReadEncodedValue(uint8_t encoding, uint64_t* value) {
+ if (encoding == DW_EH_PE_omit) {
+ *value = 0;
+ return true;
+ } else if (encoding == DW_EH_PE_aligned) {
+ if (__builtin_add_overflow(cur_offset_, sizeof(AddressType) - 1, &cur_offset_)) {
+ return false;
+ }
+ cur_offset_ &= -sizeof(AddressType);
+
+ if (sizeof(AddressType) != sizeof(uint64_t)) {
+ *value = 0;
+ }
+ return ReadBytes(value, sizeof(AddressType));
+ }
+
+ // Get the data.
+ switch (encoding & 0x0f) {
+ case DW_EH_PE_absptr:
+ if (sizeof(AddressType) != sizeof(uint64_t)) {
+ *value = 0;
+ }
+ if (!ReadBytes(value, sizeof(AddressType))) {
+ return false;
+ }
+ break;
+ case DW_EH_PE_uleb128:
+ if (!ReadULEB128(value)) {
+ return false;
+ }
+ break;
+ case DW_EH_PE_sleb128:
+ int64_t signed_value;
+ if (!ReadSLEB128(&signed_value)) {
+ return false;
+ }
+ *value = static_cast<uint64_t>(signed_value);
+ break;
+ case DW_EH_PE_udata1: {
+ uint8_t value8;
+ if (!ReadBytes(&value8, 1)) {
+ return false;
+ }
+ *value = value8;
+ } break;
+ case DW_EH_PE_sdata1:
+ if (!ReadSigned<int8_t>(value)) {
+ return false;
+ }
+ break;
+ case DW_EH_PE_udata2: {
+ uint16_t value16;
+ if (!ReadBytes(&value16, 2)) {
+ return false;
+ }
+ *value = value16;
+ } break;
+ case DW_EH_PE_sdata2:
+ if (!ReadSigned<int16_t>(value)) {
+ return false;
+ }
+ break;
+ case DW_EH_PE_udata4: {
+ uint32_t value32;
+ if (!ReadBytes(&value32, 4)) {
+ return false;
+ }
+ *value = value32;
+ } break;
+ case DW_EH_PE_sdata4:
+ if (!ReadSigned<int32_t>(value)) {
+ return false;
+ }
+ break;
+ case DW_EH_PE_udata8:
+ if (!ReadBytes(value, sizeof(uint64_t))) {
+ return false;
+ }
+ break;
+ case DW_EH_PE_sdata8:
+ if (!ReadSigned<int64_t>(value)) {
+ return false;
+ }
+ break;
+ default:
+ return false;
+ }
+
+ return AdjustEncodedValue(encoding & 0xf0, value);
+}
+
+// Instantiate all of the needed template functions.
+template bool DwarfMemory::ReadSigned<int8_t>(uint64_t*);
+template bool DwarfMemory::ReadSigned<int16_t>(uint64_t*);
+template bool DwarfMemory::ReadSigned<int32_t>(uint64_t*);
+template bool DwarfMemory::ReadSigned<int64_t>(uint64_t*);
+
+template size_t DwarfMemory::GetEncodedSize<uint32_t>(uint8_t);
+template size_t DwarfMemory::GetEncodedSize<uint64_t>(uint8_t);
+
+template bool DwarfMemory::ReadEncodedValue<uint32_t>(uint8_t, uint64_t*);
+template bool DwarfMemory::ReadEncodedValue<uint64_t>(uint8_t, uint64_t*);
diff --git a/libunwindstack/DwarfMemory.h b/libunwindstack/DwarfMemory.h
new file mode 100644
index 0000000..a304dd9
--- /dev/null
+++ b/libunwindstack/DwarfMemory.h
@@ -0,0 +1,72 @@
+/*
+ * 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 _LIBUNWINDSTACK_DWARF_MEMORY_H
+#define _LIBUNWINDSTACK_DWARF_MEMORY_H
+
+#include <stdint.h>
+
+// Forward declarations.
+class Memory;
+
+class DwarfMemory {
+ public:
+ DwarfMemory(Memory* memory) : memory_(memory) {}
+ virtual ~DwarfMemory() = default;
+
+ bool ReadBytes(void* dst, size_t num_bytes);
+
+ template <typename SignedType>
+ bool ReadSigned(uint64_t* value);
+
+ bool ReadULEB128(uint64_t* value);
+
+ bool ReadSLEB128(int64_t* value);
+
+ template <typename AddressType>
+ size_t GetEncodedSize(uint8_t encoding);
+
+ bool AdjustEncodedValue(uint8_t encoding, uint64_t* value);
+
+ template <typename AddressType>
+ bool ReadEncodedValue(uint8_t encoding, uint64_t* value);
+
+ uint64_t cur_offset() { return cur_offset_; }
+ void set_cur_offset(uint64_t cur_offset) { cur_offset_ = cur_offset; }
+
+ void set_pc_offset(uint64_t offset) { pc_offset_ = offset; }
+ void clear_pc_offset() { pc_offset_ = static_cast<uint64_t>(-1); }
+
+ void set_data_offset(uint64_t offset) { data_offset_ = offset; }
+ void clear_data_offset() { data_offset_ = static_cast<uint64_t>(-1); }
+
+ void set_func_offset(uint64_t offset) { func_offset_ = offset; }
+ void clear_func_offset() { func_offset_ = static_cast<uint64_t>(-1); }
+
+ void set_text_offset(uint64_t offset) { text_offset_ = offset; }
+ void clear_text_offset() { text_offset_ = static_cast<uint64_t>(-1); }
+
+ private:
+ Memory* memory_;
+ uint64_t cur_offset_ = 0;
+
+ uint64_t pc_offset_ = static_cast<uint64_t>(-1);
+ uint64_t data_offset_ = static_cast<uint64_t>(-1);
+ uint64_t func_offset_ = static_cast<uint64_t>(-1);
+ uint64_t text_offset_ = static_cast<uint64_t>(-1);
+};
+
+#endif // _LIBUNWINDSTACK_DWARF_MEMORY_H
diff --git a/libunwindstack/DwarfOp.cpp b/libunwindstack/DwarfOp.cpp
new file mode 100644
index 0000000..507ca08
--- /dev/null
+++ b/libunwindstack/DwarfOp.cpp
@@ -0,0 +1,462 @@
+/*
+ * 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 <deque>
+#include <string>
+#include <vector>
+
+#include <android-base/stringprintf.h>
+
+#include "DwarfError.h"
+#include "DwarfMemory.h"
+#include "DwarfOp.h"
+#include "Log.h"
+#include "Memory.h"
+#include "Regs.h"
+
+template <typename AddressType>
+constexpr typename DwarfOp<AddressType>::OpCallback DwarfOp<AddressType>::kCallbackTable[256];
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::Eval(uint64_t start, uint64_t end, uint8_t dwarf_version) {
+ uint32_t iterations = 0;
+ is_register_ = false;
+ stack_.clear();
+ memory_->set_cur_offset(start);
+ while (memory_->cur_offset() < end) {
+ if (!Decode(dwarf_version)) {
+ return false;
+ }
+ // To protect against a branch that creates an infinite loop,
+ // terminate if the number of iterations gets too high.
+ if (iterations++ == 1000) {
+ last_error_ = DWARF_ERROR_TOO_MANY_ITERATIONS;
+ return false;
+ }
+ }
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::Decode(uint8_t dwarf_version) {
+ last_error_ = DWARF_ERROR_NONE;
+ if (!memory_->ReadBytes(&cur_op_, 1)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+
+ const auto* op = &kCallbackTable[cur_op_];
+ const auto handle_func = op->handle_func;
+ if (handle_func == nullptr) {
+ last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+ return false;
+ }
+
+ // Check for an unsupported opcode.
+ if (dwarf_version < op->supported_version) {
+ last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+ return false;
+ }
+
+ // Make sure that the required number of stack elements is available.
+ if (stack_.size() < op->num_required_stack_values) {
+ last_error_ = DWARF_ERROR_STACK_INDEX_NOT_VALID;
+ return false;
+ }
+
+ operands_.clear();
+ for (size_t i = 0; i < op->num_operands; i++) {
+ uint64_t value;
+ if (!memory_->ReadEncodedValue<AddressType>(op->operands[i], &value)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ operands_.push_back(value);
+ }
+ return (this->*handle_func)();
+}
+
+template <typename AddressType>
+void DwarfOp<AddressType>::GetLogInfo(uint64_t start, uint64_t end,
+ std::vector<std::string>* lines) {
+ memory_->set_cur_offset(start);
+ while (memory_->cur_offset() < end) {
+ uint8_t cur_op;
+ if (!memory_->ReadBytes(&cur_op, 1)) {
+ return;
+ }
+
+ std::string raw_string(android::base::StringPrintf("Raw Data: 0x%02x", cur_op));
+ std::string log_string;
+ const auto* op = &kCallbackTable[cur_op];
+ if (op->handle_func == nullptr) {
+ log_string = "Illegal";
+ } else {
+ log_string = op->name;
+ uint64_t start_offset = memory_->cur_offset();
+ for (size_t i = 0; i < op->num_operands; i++) {
+ uint64_t value;
+ if (!memory_->ReadEncodedValue<AddressType>(op->operands[i], &value)) {
+ return;
+ }
+ log_string += ' ' + std::to_string(value);
+ }
+ uint64_t end_offset = memory_->cur_offset();
+
+ memory_->set_cur_offset(start_offset);
+ for (size_t i = start_offset; i < end_offset; i++) {
+ uint8_t byte;
+ if (!memory_->ReadBytes(&byte, 1)) {
+ return;
+ }
+ raw_string += android::base::StringPrintf(" 0x%02x", byte);
+ }
+ memory_->set_cur_offset(end_offset);
+ }
+ lines->push_back(std::move(log_string));
+ lines->push_back(std::move(raw_string));
+ }
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_deref() {
+ // Read the address and dereference it.
+ AddressType addr = StackPop();
+ AddressType value;
+ if (!regular_memory()->Read(addr, &value, sizeof(value))) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ stack_.push_front(value);
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_deref_size() {
+ AddressType bytes_to_read = OperandAt(0);
+ if (bytes_to_read > sizeof(AddressType) || bytes_to_read == 0) {
+ last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+ return false;
+ }
+ // Read the address and dereference it.
+ AddressType addr = StackPop();
+ AddressType value = 0;
+ if (!regular_memory()->Read(addr, &value, bytes_to_read)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ stack_.push_front(value);
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_push() {
+ // Push all of the operands.
+ for (auto operand : operands_) {
+ stack_.push_front(operand);
+ }
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_dup() {
+ stack_.push_front(StackAt(0));
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_drop() {
+ StackPop();
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_over() {
+ stack_.push_front(StackAt(1));
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_pick() {
+ AddressType index = OperandAt(0);
+ if (index > StackSize()) {
+ last_error_ = DWARF_ERROR_STACK_INDEX_NOT_VALID;
+ return false;
+ }
+ stack_.push_front(StackAt(index));
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_swap() {
+ AddressType old_value = stack_[0];
+ stack_[0] = stack_[1];
+ stack_[1] = old_value;
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_rot() {
+ AddressType top = stack_[0];
+ stack_[0] = stack_[1];
+ stack_[1] = stack_[2];
+ stack_[2] = top;
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_abs() {
+ SignedType signed_value = static_cast<SignedType>(stack_[0]);
+ if (signed_value < 0) {
+ signed_value = -signed_value;
+ }
+ stack_[0] = static_cast<AddressType>(signed_value);
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_and() {
+ AddressType top = StackPop();
+ stack_[0] &= top;
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_div() {
+ AddressType top = StackPop();
+ if (top == 0) {
+ last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+ return false;
+ }
+ SignedType signed_divisor = static_cast<SignedType>(top);
+ SignedType signed_dividend = static_cast<SignedType>(stack_[0]);
+ stack_[0] = static_cast<AddressType>(signed_dividend / signed_divisor);
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_minus() {
+ AddressType top = StackPop();
+ stack_[0] -= top;
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_mod() {
+ AddressType top = StackPop();
+ if (top == 0) {
+ last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+ return false;
+ }
+ stack_[0] %= top;
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_mul() {
+ AddressType top = StackPop();
+ stack_[0] *= top;
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_neg() {
+ SignedType signed_value = static_cast<SignedType>(stack_[0]);
+ stack_[0] = static_cast<AddressType>(-signed_value);
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_not() {
+ stack_[0] = ~stack_[0];
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_or() {
+ AddressType top = StackPop();
+ stack_[0] |= top;
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_plus() {
+ AddressType top = StackPop();
+ stack_[0] += top;
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_plus_uconst() {
+ stack_[0] += OperandAt(0);
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_shl() {
+ AddressType top = StackPop();
+ stack_[0] <<= top;
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_shr() {
+ AddressType top = StackPop();
+ stack_[0] >>= top;
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_shra() {
+ AddressType top = StackPop();
+ SignedType signed_value = static_cast<SignedType>(stack_[0]) >> top;
+ stack_[0] = static_cast<AddressType>(signed_value);
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_xor() {
+ AddressType top = StackPop();
+ stack_[0] ^= top;
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_bra() {
+ // Requires one stack element.
+ AddressType top = StackPop();
+ int16_t offset = static_cast<int16_t>(OperandAt(0));
+ uint64_t cur_offset;
+ if (top != 0) {
+ cur_offset = memory_->cur_offset() + offset;
+ } else {
+ cur_offset = memory_->cur_offset() - offset;
+ }
+ memory_->set_cur_offset(cur_offset);
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_eq() {
+ AddressType top = StackPop();
+ stack_[0] = bool_to_dwarf_bool(stack_[0] == top);
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_ge() {
+ AddressType top = StackPop();
+ stack_[0] = bool_to_dwarf_bool(stack_[0] >= top);
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_gt() {
+ AddressType top = StackPop();
+ stack_[0] = bool_to_dwarf_bool(stack_[0] > top);
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_le() {
+ AddressType top = StackPop();
+ stack_[0] = bool_to_dwarf_bool(stack_[0] <= top);
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_lt() {
+ AddressType top = StackPop();
+ stack_[0] = bool_to_dwarf_bool(stack_[0] < top);
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_ne() {
+ AddressType top = StackPop();
+ stack_[0] = bool_to_dwarf_bool(stack_[0] != top);
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_skip() {
+ int16_t offset = static_cast<int16_t>(OperandAt(0));
+ uint64_t cur_offset = memory_->cur_offset() + offset;
+ memory_->set_cur_offset(cur_offset);
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_lit() {
+ stack_.push_front(cur_op() - 0x30);
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_reg() {
+ is_register_ = true;
+ stack_.push_front(cur_op() - 0x50);
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_regx() {
+ is_register_ = true;
+ stack_.push_front(OperandAt(0));
+ return true;
+}
+
+// It's not clear for breg/bregx, if this op should read the current
+// value of the register, or where we think that register is located.
+// For simplicity, the code will read the value before doing the unwind.
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_breg() {
+ uint16_t reg = cur_op() - 0x70;
+ if (reg >= regs_->total_regs()) {
+ last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+ return false;
+ }
+ stack_.push_front((*regs_)[reg] + OperandAt(0));
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_bregx() {
+ AddressType reg = OperandAt(0);
+ if (reg >= regs_->total_regs()) {
+ last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
+ return false;
+ }
+ stack_.push_front((*regs_)[reg] + OperandAt(1));
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_nop() {
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfOp<AddressType>::op_not_implemented() {
+ last_error_ = DWARF_ERROR_NOT_IMPLEMENTED;
+ return false;
+}
+
+// Explicitly instantiate DwarfOp.
+template class DwarfOp<uint32_t>;
+template class DwarfOp<uint64_t>;
diff --git a/libunwindstack/DwarfOp.h b/libunwindstack/DwarfOp.h
new file mode 100644
index 0000000..ed6537a
--- /dev/null
+++ b/libunwindstack/DwarfOp.h
@@ -0,0 +1,1636 @@
+/*
+ * 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 _LIBUNWINDSTACK_DWARF_OP_H
+#define _LIBUNWINDSTACK_DWARF_OP_H
+
+#include <stdint.h>
+
+#include <deque>
+#include <string>
+#include <type_traits>
+#include <vector>
+
+#include "DwarfEncoding.h"
+
+enum DwarfVersion : uint8_t {
+ DWARF_VERSION_2 = 2,
+ DWARF_VERSION_3 = 3,
+ DWARF_VERSION_4 = 4,
+ DWARF_VERSION_MAX = DWARF_VERSION_4,
+};
+
+// Forward declarations.
+class DwarfMemory;
+class Memory;
+template <typename AddressType>
+class RegsTmpl;
+
+template <typename AddressType>
+class DwarfOp {
+ // Signed version of AddressType
+ typedef typename std::make_signed<AddressType>::type SignedType;
+
+ struct OpCallback {
+ const char* name;
+ bool (DwarfOp::*handle_func)();
+ uint8_t supported_version;
+ uint8_t num_required_stack_values;
+ uint8_t num_operands;
+ uint8_t operands[2];
+ };
+
+ public:
+ DwarfOp(DwarfMemory* memory, Memory* regular_memory)
+ : memory_(memory), regular_memory_(regular_memory) {}
+ virtual ~DwarfOp() = default;
+
+ bool Decode(uint8_t dwarf_version);
+
+ bool Eval(uint64_t start, uint64_t end, uint8_t dwarf_version);
+
+ void GetLogInfo(uint64_t start, uint64_t end, std::vector<std::string>* lines);
+
+ AddressType StackAt(size_t index) { return stack_[index]; }
+ size_t StackSize() { return stack_.size(); }
+
+ void set_regs(RegsTmpl<AddressType>* regs) { regs_ = regs; }
+
+ DwarfError last_error() { return last_error_; }
+
+ bool is_register() { return is_register_; }
+
+ uint8_t cur_op() { return cur_op_; }
+
+ Memory* regular_memory() { return regular_memory_; }
+
+ protected:
+ AddressType OperandAt(size_t index) { return operands_[index]; }
+ size_t OperandsSize() { return operands_.size(); }
+
+ AddressType StackPop() {
+ AddressType value = stack_.front();
+ stack_.pop_front();
+ return value;
+ }
+
+ private:
+ DwarfMemory* memory_;
+ Memory* regular_memory_;
+
+ RegsTmpl<AddressType>* regs_;
+ bool is_register_ = false;
+ DwarfError last_error_ = DWARF_ERROR_NONE;
+ uint8_t cur_op_;
+ std::vector<AddressType> operands_;
+ std::deque<AddressType> stack_;
+
+ inline AddressType bool_to_dwarf_bool(bool value) { return value ? 1 : 0; }
+
+ // Op processing functions.
+ bool op_deref();
+ bool op_deref_size();
+ bool op_push();
+ bool op_dup();
+ bool op_drop();
+ bool op_over();
+ bool op_pick();
+ bool op_swap();
+ bool op_rot();
+ bool op_abs();
+ bool op_and();
+ bool op_div();
+ bool op_minus();
+ bool op_mod();
+ bool op_mul();
+ bool op_neg();
+ bool op_not();
+ bool op_or();
+ bool op_plus();
+ bool op_plus_uconst();
+ bool op_shl();
+ bool op_shr();
+ bool op_shra();
+ bool op_xor();
+ bool op_bra();
+ bool op_eq();
+ bool op_ge();
+ bool op_gt();
+ bool op_le();
+ bool op_lt();
+ bool op_ne();
+ bool op_skip();
+ bool op_lit();
+ bool op_reg();
+ bool op_regx();
+ bool op_breg();
+ bool op_bregx();
+ bool op_nop();
+ bool op_not_implemented();
+
+ constexpr static OpCallback kCallbackTable[256] = {
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0x00 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0x01 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0x02 illegal op
+ {
+ // 0x03 DW_OP_addr
+ "DW_OP_addr",
+ &DwarfOp::op_push,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_absptr},
+ },
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0x04 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0x05 illegal op
+ {
+ // 0x06 DW_OP_deref
+ "DW_OP_deref",
+ &DwarfOp::op_deref,
+ DWARF_VERSION_2,
+ 1,
+ 0,
+ {},
+ },
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0x07 illegal op
+ {
+ // 0x08 DW_OP_const1u
+ "DW_OP_const1u",
+ &DwarfOp::op_push,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_udata1},
+ },
+ {
+ // 0x09 DW_OP_const1s
+ "DW_OP_const1s",
+ &DwarfOp::op_push,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sdata1},
+ },
+ {
+ // 0x0a DW_OP_const2u
+ "DW_OP_const2u",
+ &DwarfOp::op_push,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_udata2},
+ },
+ {
+ // 0x0b DW_OP_const2s
+ "DW_OP_const2s",
+ &DwarfOp::op_push,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sdata2},
+ },
+ {
+ // 0x0c DW_OP_const4u
+ "DW_OP_const4u",
+ &DwarfOp::op_push,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_udata4},
+ },
+ {
+ // 0x0d DW_OP_const4s
+ "DW_OP_const4s",
+ &DwarfOp::op_push,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sdata4},
+ },
+ {
+ // 0x0e DW_OP_const8u
+ "DW_OP_const8u",
+ &DwarfOp::op_push,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_udata8},
+ },
+ {
+ // 0x0f DW_OP_const8s
+ "DW_OP_const8s",
+ &DwarfOp::op_push,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sdata8},
+ },
+ {
+ // 0x10 DW_OP_constu
+ "DW_OP_constu",
+ &DwarfOp::op_push,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_uleb128},
+ },
+ {
+ // 0x11 DW_OP_consts
+ "DW_OP_consts",
+ &DwarfOp::op_push,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x12 DW_OP_dup
+ "DW_OP_dup",
+ &DwarfOp::op_dup,
+ DWARF_VERSION_2,
+ 1,
+ 0,
+ {},
+ },
+ {
+ // 0x13 DW_OP_drop
+ "DW_OP_drop",
+ &DwarfOp::op_drop,
+ DWARF_VERSION_2,
+ 1,
+ 0,
+ {},
+ },
+ {
+ // 0x14 DW_OP_over
+ "DW_OP_over",
+ &DwarfOp::op_over,
+ DWARF_VERSION_2,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x15 DW_OP_pick
+ "DW_OP_pick",
+ &DwarfOp::op_pick,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_udata1},
+ },
+ {
+ // 0x16 DW_OP_swap
+ "DW_OP_swap",
+ &DwarfOp::op_swap,
+ DWARF_VERSION_2,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x17 DW_OP_rot
+ "DW_OP_rot",
+ &DwarfOp::op_rot,
+ DWARF_VERSION_2,
+ 3,
+ 0,
+ {},
+ },
+ {
+ // 0x18 DW_OP_xderef
+ "DW_OP_xderef",
+ &DwarfOp::op_not_implemented,
+ DWARF_VERSION_2,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x19 DW_OP_abs
+ "DW_OP_abs",
+ &DwarfOp::op_abs,
+ DWARF_VERSION_2,
+ 1,
+ 0,
+ {},
+ },
+ {
+ // 0x1a DW_OP_and
+ "DW_OP_and",
+ &DwarfOp::op_and,
+ DWARF_VERSION_2,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x1b DW_OP_div
+ "DW_OP_div",
+ &DwarfOp::op_div,
+ DWARF_VERSION_2,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x1c DW_OP_minus
+ "DW_OP_minus",
+ &DwarfOp::op_minus,
+ DWARF_VERSION_2,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x1d DW_OP_mod
+ "DW_OP_mod",
+ &DwarfOp::op_mod,
+ DWARF_VERSION_2,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x1e DW_OP_mul
+ "DW_OP_mul",
+ &DwarfOp::op_mul,
+ DWARF_VERSION_2,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x1f DW_OP_neg
+ "DW_OP_neg",
+ &DwarfOp::op_neg,
+ DWARF_VERSION_2,
+ 1,
+ 0,
+ {},
+ },
+ {
+ // 0x20 DW_OP_not
+ "DW_OP_not",
+ &DwarfOp::op_not,
+ DWARF_VERSION_2,
+ 1,
+ 0,
+ {},
+ },
+ {
+ // 0x21 DW_OP_or
+ "DW_OP_or",
+ &DwarfOp::op_or,
+ DWARF_VERSION_2,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x22 DW_OP_plus
+ "DW_OP_plus",
+ &DwarfOp::op_plus,
+ DWARF_VERSION_2,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x23 DW_OP_plus_uconst
+ "DW_OP_plus_uconst",
+ &DwarfOp::op_plus_uconst,
+ DWARF_VERSION_2,
+ 1,
+ 1,
+ {DW_EH_PE_uleb128},
+ },
+ {
+ // 0x24 DW_OP_shl
+ "DW_OP_shl",
+ &DwarfOp::op_shl,
+ DWARF_VERSION_2,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x25 DW_OP_shr
+ "DW_OP_shr",
+ &DwarfOp::op_shr,
+ DWARF_VERSION_2,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x26 DW_OP_shra
+ "DW_OP_shra",
+ &DwarfOp::op_shra,
+ DWARF_VERSION_2,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x27 DW_OP_xor
+ "DW_OP_xor",
+ &DwarfOp::op_xor,
+ DWARF_VERSION_2,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x28 DW_OP_bra
+ "DW_OP_bra",
+ &DwarfOp::op_bra,
+ DWARF_VERSION_2,
+ 1,
+ 1,
+ {DW_EH_PE_sdata2},
+ },
+ {
+ // 0x29 DW_OP_eq
+ "DW_OP_eq",
+ &DwarfOp::op_eq,
+ DWARF_VERSION_2,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x2a DW_OP_ge
+ "DW_OP_ge",
+ &DwarfOp::op_ge,
+ DWARF_VERSION_2,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x2b DW_OP_gt
+ "DW_OP_gt",
+ &DwarfOp::op_gt,
+ DWARF_VERSION_2,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x2c DW_OP_le
+ "DW_OP_le",
+ &DwarfOp::op_le,
+ DWARF_VERSION_2,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x2d DW_OP_lt
+ "DW_OP_lt",
+ &DwarfOp::op_lt,
+ DWARF_VERSION_2,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x2e DW_OP_ne
+ "DW_OP_ne",
+ &DwarfOp::op_ne,
+ DWARF_VERSION_2,
+ 2,
+ 0,
+ {},
+ },
+ {
+ // 0x2f DW_OP_skip
+ "DW_OP_skip",
+ &DwarfOp::op_skip,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sdata2},
+ },
+ {
+ // 0x30 DW_OP_lit0
+ "DW_OP_lit0",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x31 DW_OP_lit1
+ "DW_OP_lit1",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x32 DW_OP_lit2
+ "DW_OP_lit2",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x33 DW_OP_lit3
+ "DW_OP_lit3",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x34 DW_OP_lit4
+ "DW_OP_lit4",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x35 DW_OP_lit5
+ "DW_OP_lit5",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x36 DW_OP_lit6
+ "DW_OP_lit6",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x37 DW_OP_lit7
+ "DW_OP_lit7",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x38 DW_OP_lit8
+ "DW_OP_lit8",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x39 DW_OP_lit9
+ "DW_OP_lit9",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x3a DW_OP_lit10
+ "DW_OP_lit10",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x3b DW_OP_lit11
+ "DW_OP_lit11",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x3c DW_OP_lit12
+ "DW_OP_lit12",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x3d DW_OP_lit13
+ "DW_OP_lit13",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x3e DW_OP_lit14
+ "DW_OP_lit14",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x3f DW_OP_lit15
+ "DW_OP_lit15",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x40 DW_OP_lit16
+ "DW_OP_lit16",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x41 DW_OP_lit17
+ "DW_OP_lit17",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x42 DW_OP_lit18
+ "DW_OP_lit18",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x43 DW_OP_lit19
+ "DW_OP_lit19",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x44 DW_OP_lit20
+ "DW_OP_lit20",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x45 DW_OP_lit21
+ "DW_OP_lit21",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x46 DW_OP_lit22
+ "DW_OP_lit22",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x47 DW_OP_lit23
+ "DW_OP_lit23",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x48 DW_OP_lit24
+ "DW_OP_lit24",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x49 DW_OP_lit25
+ "DW_OP_lit25",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x4a DW_OP_lit26
+ "DW_OP_lit26",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x4b DW_OP_lit27
+ "DW_OP_lit27",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x4c DW_OP_lit28
+ "DW_OP_lit28",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x4d DW_OP_lit29
+ "DW_OP_lit29",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x4e DW_OP_lit30
+ "DW_OP_lit30",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x4f DW_OP_lit31
+ "DW_OP_lit31",
+ &DwarfOp::op_lit,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x50 DW_OP_reg0
+ "DW_OP_reg0",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x51 DW_OP_reg1
+ "DW_OP_reg1",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x52 DW_OP_reg2
+ "DW_OP_reg2",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x53 DW_OP_reg3
+ "DW_OP_reg3",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x54 DW_OP_reg4
+ "DW_OP_reg4",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x55 DW_OP_reg5
+ "DW_OP_reg5",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x56 DW_OP_reg6
+ "DW_OP_reg6",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x57 DW_OP_reg7
+ "DW_OP_reg7",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x58 DW_OP_reg8
+ "DW_OP_reg8",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x59 DW_OP_reg9
+ "DW_OP_reg9",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x5a DW_OP_reg10
+ "DW_OP_reg10",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x5b DW_OP_reg11
+ "DW_OP_reg11",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x5c DW_OP_reg12
+ "DW_OP_reg12",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x5d DW_OP_reg13
+ "DW_OP_reg13",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x5e DW_OP_reg14
+ "DW_OP_reg14",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x5f DW_OP_reg15
+ "DW_OP_reg15",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x60 DW_OP_reg16
+ "DW_OP_reg16",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x61 DW_OP_reg17
+ "DW_OP_reg17",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x62 DW_OP_reg18
+ "DW_OP_reg18",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x63 DW_OP_reg19
+ "DW_OP_reg19",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x64 DW_OP_reg20
+ "DW_OP_reg20",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x65 DW_OP_reg21
+ "DW_OP_reg21",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x66 DW_OP_reg22
+ "DW_OP_reg22",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x67 DW_OP_reg23
+ "DW_OP_reg23",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x68 DW_OP_reg24
+ "DW_OP_reg24",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x69 DW_OP_reg25
+ "DW_OP_reg25",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x6a DW_OP_reg26
+ "DW_OP_reg26",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x6b DW_OP_reg27
+ "DW_OP_reg27",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x6c DW_OP_reg28
+ "DW_OP_reg28",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x6d DW_OP_reg29
+ "DW_OP_reg29",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x6e DW_OP_reg30
+ "DW_OP_reg30",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x6f DW_OP_reg31
+ "DW_OP_reg31",
+ &DwarfOp::op_reg,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x70 DW_OP_breg0
+ "DW_OP_breg0",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x71 DW_OP_breg1
+ "DW_OP_breg1",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x72 DW_OP_breg2
+ "DW_OP_breg2",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x73 DW_OP_breg3
+ "DW_OP_breg3",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x74 DW_OP_breg4
+ "DW_OP_breg4",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x75 DW_OP_breg5
+ "DW_OP_breg5",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x76 DW_OP_breg6
+ "DW_OP_breg6",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x77 DW_OP_breg7
+ "DW_OP_breg7",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x78 DW_OP_breg8
+ "DW_OP_breg8",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x79 DW_OP_breg9
+ "DW_OP_breg9",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x7a DW_OP_breg10
+ "DW_OP_breg10",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x7b DW_OP_breg11
+ "DW_OP_breg11",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x7c DW_OP_breg12
+ "DW_OP_breg12",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x7d DW_OP_breg13
+ "DW_OP_breg13",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x7e DW_OP_breg14
+ "DW_OP_breg14",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x7f DW_OP_breg15
+ "DW_OP_breg15",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x80 DW_OP_breg16
+ "DW_OP_breg16",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x81 DW_OP_breg17
+ "DW_OP_breg17",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x82 DW_OP_breg18
+ "DW_OP_breg18",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x83 DW_OP_breg19
+ "DW_OP_breg19",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x84 DW_OP_breg20
+ "DW_OP_breg20",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x85 DW_OP_breg21
+ "DW_OP_breg21",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x86 DW_OP_breg22
+ "DW_OP_breg22",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x87 DW_OP_breg23
+ "DW_OP_breg23",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x88 DW_OP_breg24
+ "DW_OP_breg24",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x89 DW_OP_breg25
+ "DW_OP_breg25",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x8a DW_OP_breg26
+ "DW_OP_breg26",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x8b DW_OP_breg27
+ "DW_OP_breg27",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x8c DW_OP_breg28
+ "DW_OP_breg28",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x8d DW_OP_breg29
+ "DW_OP_breg29",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x8e DW_OP_breg30
+ "DW_OP_breg30",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x8f DW_OP_breg31
+ "DW_OP_breg31",
+ &DwarfOp::op_breg,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x90 DW_OP_regx
+ "DW_OP_regx",
+ &DwarfOp::op_regx,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_uleb128},
+ },
+ {
+ // 0x91 DW_OP_fbreg
+ "DW_OP_fbreg",
+ &DwarfOp::op_not_implemented,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_sleb128},
+ },
+ {
+ // 0x92 DW_OP_bregx
+ "DW_OP_bregx",
+ &DwarfOp::op_bregx,
+ DWARF_VERSION_2,
+ 0,
+ 2,
+ {DW_EH_PE_uleb128, DW_EH_PE_sleb128},
+ },
+ {
+ // 0x93 DW_OP_piece
+ "DW_OP_piece",
+ &DwarfOp::op_not_implemented,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_uleb128},
+ },
+ {
+ // 0x94 DW_OP_deref_size
+ "DW_OP_deref_size",
+ &DwarfOp::op_deref_size,
+ DWARF_VERSION_2,
+ 1,
+ 1,
+ {DW_EH_PE_udata1},
+ },
+ {
+ // 0x95 DW_OP_xderef_size
+ "DW_OP_xderef_size",
+ &DwarfOp::op_not_implemented,
+ DWARF_VERSION_2,
+ 0,
+ 1,
+ {DW_EH_PE_udata1},
+ },
+ {
+ // 0x96 DW_OP_nop
+ "DW_OP_nop",
+ &DwarfOp::op_nop,
+ DWARF_VERSION_2,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x97 DW_OP_push_object_address
+ "DW_OP_push_object_address",
+ &DwarfOp::op_not_implemented,
+ DWARF_VERSION_3,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x98 DW_OP_call2
+ "DW_OP_call2",
+ &DwarfOp::op_not_implemented,
+ DWARF_VERSION_3,
+ 0,
+ 1,
+ {DW_EH_PE_udata2},
+ },
+ {
+ // 0x99 DW_OP_call4
+ "DW_OP_call4",
+ &DwarfOp::op_not_implemented,
+ DWARF_VERSION_3,
+ 0,
+ 1,
+ {DW_EH_PE_udata4},
+ },
+ {
+ // 0x9a DW_OP_call_ref
+ "DW_OP_call_ref",
+ &DwarfOp::op_not_implemented,
+ DWARF_VERSION_3,
+ 0,
+ 0, // Has a different sized operand (4 bytes or 8 bytes).
+ {},
+ },
+ {
+ // 0x9b DW_OP_form_tls_address
+ "DW_OP_form_tls_address",
+ &DwarfOp::op_not_implemented,
+ DWARF_VERSION_3,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x9c DW_OP_call_frame_cfa
+ "DW_OP_call_frame_cfa",
+ &DwarfOp::op_not_implemented,
+ DWARF_VERSION_3,
+ 0,
+ 0,
+ {},
+ },
+ {
+ // 0x9d DW_OP_bit_piece
+ "DW_OP_bit_piece",
+ &DwarfOp::op_not_implemented,
+ DWARF_VERSION_3,
+ 0,
+ 2,
+ {DW_EH_PE_uleb128, DW_EH_PE_uleb128},
+ },
+ {
+ // 0x9e DW_OP_implicit_value
+ "DW_OP_implicit_value",
+ &DwarfOp::op_not_implemented,
+ DWARF_VERSION_4,
+ 0,
+ 1,
+ {DW_EH_PE_uleb128},
+ },
+ {
+ // 0x9f DW_OP_stack_value
+ "DW_OP_stack_value",
+ &DwarfOp::op_not_implemented,
+ DWARF_VERSION_4,
+ 1,
+ 0,
+ {},
+ },
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xa0 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xa1 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xa2 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xa3 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xa4 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xa5 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xa6 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xa7 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xa8 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xa9 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xaa illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xab illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xac illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xad illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xae illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xaf illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xb0 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xb1 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xb2 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xb3 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xb4 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xb5 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xb6 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xb7 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xb8 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xb9 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xba illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xbb illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xbc illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xbd illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xbe illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xbf illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xc0 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xc1 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xc2 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xc3 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xc4 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xc5 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xc6 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xc7 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xc8 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xc9 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xca illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xcb illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xcc illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xcd illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xce illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xcf illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xd0 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xd1 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xd2 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xd3 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xd4 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xd5 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xd6 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xd7 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xd8 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xd9 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xda illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xdb illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xdc illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xdd illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xde illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xdf illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xe0 DW_OP_lo_user
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xe1 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xe2 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xe3 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xe4 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xe5 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xe6 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xe7 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xe8 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xe9 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xea illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xeb illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xec illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xed illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xee illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xef illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xf0 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xf1 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xf2 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xf3 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xf4 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xf5 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xf6 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xf7 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xf8 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xf9 illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xfa illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xfb illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xfc illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xfd illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xfe illegal op
+ {nullptr, nullptr, 0, 0, 0, {}}, // 0xff DW_OP_hi_user
+ };
+};
+
+#endif // _LIBUNWINDSTACK_DWARF_OP_H
diff --git a/libunwindstack/ElfInterface.cpp b/libunwindstack/ElfInterface.cpp
index d59e9d8..087457c 100644
--- a/libunwindstack/ElfInterface.cpp
+++ b/libunwindstack/ElfInterface.cpp
@@ -42,7 +42,7 @@
uint64_t offset = ehdr.e_phoff;
for (size_t i = 0; i < ehdr.e_phnum; i++, offset += ehdr.e_phentsize) {
PhdrType phdr;
- if (!memory_->Read(offset, &phdr, &phdr.p_type, sizeof(phdr.p_type))) {
+ if (!memory_->ReadField(offset, &phdr, &phdr.p_type, sizeof(phdr.p_type))) {
return false;
}
@@ -54,20 +54,20 @@
case PT_LOAD:
{
// Get the flags first, if this isn't an executable header, ignore it.
- if (!memory_->Read(offset, &phdr, &phdr.p_flags, sizeof(phdr.p_flags))) {
+ if (!memory_->ReadField(offset, &phdr, &phdr.p_flags, sizeof(phdr.p_flags))) {
return false;
}
if ((phdr.p_flags & PF_X) == 0) {
continue;
}
- if (!memory_->Read(offset, &phdr, &phdr.p_vaddr, sizeof(phdr.p_vaddr))) {
+ if (!memory_->ReadField(offset, &phdr, &phdr.p_vaddr, sizeof(phdr.p_vaddr))) {
return false;
}
- if (!memory_->Read(offset, &phdr, &phdr.p_offset, sizeof(phdr.p_offset))) {
+ if (!memory_->ReadField(offset, &phdr, &phdr.p_offset, sizeof(phdr.p_offset))) {
return false;
}
- if (!memory_->Read(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) {
+ if (!memory_->ReadField(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) {
return false;
}
pt_loads_[phdr.p_offset] = LoadInfo{phdr.p_offset, phdr.p_vaddr,
@@ -79,22 +79,22 @@
}
case PT_GNU_EH_FRAME:
- if (!memory_->Read(offset, &phdr, &phdr.p_offset, sizeof(phdr.p_offset))) {
+ if (!memory_->ReadField(offset, &phdr, &phdr.p_offset, sizeof(phdr.p_offset))) {
return false;
}
eh_frame_offset_ = phdr.p_offset;
- if (!memory_->Read(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) {
+ if (!memory_->ReadField(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) {
return false;
}
eh_frame_size_ = phdr.p_memsz;
break;
case PT_DYNAMIC:
- if (!memory_->Read(offset, &phdr, &phdr.p_offset, sizeof(phdr.p_offset))) {
+ if (!memory_->ReadField(offset, &phdr, &phdr.p_offset, sizeof(phdr.p_offset))) {
return false;
}
dynamic_offset_ = phdr.p_offset;
- if (!memory_->Read(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) {
+ if (!memory_->ReadField(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) {
return false;
}
dynamic_size_ = phdr.p_memsz;
@@ -116,8 +116,8 @@
ShdrType shdr;
if (ehdr.e_shstrndx < ehdr.e_shnum) {
uint64_t sh_offset = offset + ehdr.e_shstrndx * ehdr.e_shentsize;
- if (memory_->Read(sh_offset, &shdr, &shdr.sh_offset, sizeof(shdr.sh_offset))
- && memory_->Read(sh_offset, &shdr, &shdr.sh_size, sizeof(shdr.sh_size))) {
+ if (memory_->ReadField(sh_offset, &shdr, &shdr.sh_offset, sizeof(shdr.sh_offset)) &&
+ memory_->ReadField(sh_offset, &shdr, &shdr.sh_size, sizeof(shdr.sh_size))) {
sec_offset = shdr.sh_offset;
sec_size = shdr.sh_size;
}
@@ -125,27 +125,27 @@
// Skip the first header, it's always going to be NULL.
for (size_t i = 1; i < ehdr.e_shnum; i++, offset += ehdr.e_shentsize) {
- if (!memory_->Read(offset, &shdr, &shdr.sh_type, sizeof(shdr.sh_type))) {
+ if (!memory_->ReadField(offset, &shdr, &shdr.sh_type, sizeof(shdr.sh_type))) {
return false;
}
if (shdr.sh_type == SHT_PROGBITS) {
// Look for the .debug_frame and .gnu_debugdata.
- if (!memory_->Read(offset, &shdr, &shdr.sh_name, sizeof(shdr.sh_name))) {
+ if (!memory_->ReadField(offset, &shdr, &shdr.sh_name, sizeof(shdr.sh_name))) {
return false;
}
if (shdr.sh_name < sec_size) {
std::string name;
if (memory_->ReadString(sec_offset + shdr.sh_name, &name)) {
if (name == ".debug_frame") {
- if (memory_->Read(offset, &shdr, &shdr.sh_offset, sizeof(shdr.sh_offset))
- && memory_->Read(offset, &shdr, &shdr.sh_size, sizeof(shdr.sh_size))) {
+ if (memory_->ReadField(offset, &shdr, &shdr.sh_offset, sizeof(shdr.sh_offset)) &&
+ memory_->ReadField(offset, &shdr, &shdr.sh_size, sizeof(shdr.sh_size))) {
debug_frame_offset_ = shdr.sh_offset;
debug_frame_size_ = shdr.sh_size;
}
} else if (name == ".gnu_debugdata") {
- if (memory_->Read(offset, &shdr, &shdr.sh_offset, sizeof(shdr.sh_offset))
- && memory_->Read(offset, &shdr, &shdr.sh_size, sizeof(shdr.sh_size))) {
+ if (memory_->ReadField(offset, &shdr, &shdr.sh_offset, sizeof(shdr.sh_offset)) &&
+ memory_->ReadField(offset, &shdr, &shdr.sh_size, sizeof(shdr.sh_size))) {
gnu_debugdata_offset_ = shdr.sh_offset;
gnu_debugdata_size_ = shdr.sh_size;
}
diff --git a/libunwindstack/ElfInterfaceArm.cpp b/libunwindstack/ElfInterfaceArm.cpp
index e157320..bab84cc 100644
--- a/libunwindstack/ElfInterfaceArm.cpp
+++ b/libunwindstack/ElfInterfaceArm.cpp
@@ -85,10 +85,10 @@
}
Elf32_Phdr phdr;
- if (!memory_->Read(offset, &phdr, &phdr.p_vaddr, sizeof(phdr.p_vaddr))) {
+ if (!memory_->ReadField(offset, &phdr, &phdr.p_vaddr, sizeof(phdr.p_vaddr))) {
return true;
}
- if (!memory_->Read(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) {
+ if (!memory_->ReadField(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) {
return true;
}
// The load_bias_ should always be set by this time.
@@ -98,13 +98,15 @@
}
bool ElfInterfaceArm::Step(uint64_t pc, Regs* regs, Memory* process_memory) {
- return StepExidx(pc, regs, process_memory) ||
- ElfInterface32::Step(pc, regs, process_memory);
+ // Dwarf unwind information is precise about whether a pc is covered or not,
+ // but arm unwind information only has ranges of pc. In order to avoid
+ // incorrectly doing a bad unwind using arm unwind information for a
+ // different function, always try and unwind with the dwarf information first.
+ return ElfInterface32::Step(pc, regs, process_memory) || StepExidx(pc, regs, process_memory);
}
bool ElfInterfaceArm::StepExidx(uint64_t pc, Regs* regs, Memory* process_memory) {
RegsArm* regs_arm = reinterpret_cast<RegsArm*>(regs);
- // First try arm, then try dwarf.
uint64_t entry_offset;
if (!FindEntry(pc, &entry_offset)) {
return false;
diff --git a/libunwindstack/Memory.cpp b/libunwindstack/Memory.cpp
index 1fcf842..9e46509 100644
--- a/libunwindstack/Memory.cpp
+++ b/libunwindstack/Memory.cpp
@@ -96,10 +96,16 @@
offset_ = offset & (getpagesize() - 1);
uint64_t aligned_offset = offset & ~(getpagesize() - 1);
+ if (aligned_offset > static_cast<uint64_t>(buf.st_size) ||
+ offset > static_cast<uint64_t>(buf.st_size)) {
+ return false;
+ }
+
size_ = buf.st_size - aligned_offset;
- if (size < (UINT64_MAX - offset_) && size + offset_ < size_) {
+ uint64_t max_size;
+ if (!__builtin_add_overflow(size, offset_, &max_size) && max_size < size_) {
// Truncate the mapped size.
- size_ = size + offset_;
+ size_ = max_size;
}
void* map = mmap(nullptr, size_, PROT_READ, MAP_PRIVATE, fd, aligned_offset);
if (map == MAP_FAILED) {
@@ -113,14 +119,15 @@
}
bool MemoryFileAtOffset::Read(uint64_t addr, void* dst, size_t size) {
- if (addr + size > size_) {
+ uint64_t max_size;
+ if (__builtin_add_overflow(addr, size, &max_size) || max_size > size_) {
return false;
}
memcpy(dst, &data_[addr], size);
return true;
}
-static bool PtraceRead(pid_t pid, uint64_t addr, long* value) {
+bool MemoryRemote::PtraceRead(uint64_t addr, long* value) {
#if !defined(__LP64__)
// Cannot read an address greater than 32 bits.
if (addr > UINT32_MAX) {
@@ -130,7 +137,7 @@
// ptrace() returns -1 and sets errno when the operation fails.
// To disambiguate -1 from a valid result, we clear errno beforehand.
errno = 0;
- *value = ptrace(PTRACE_PEEKTEXT, pid, reinterpret_cast<void*>(addr), nullptr);
+ *value = ptrace(PTRACE_PEEKTEXT, pid_, reinterpret_cast<void*>(addr), nullptr);
if (*value == -1 && errno) {
return false;
}
@@ -138,11 +145,17 @@
}
bool MemoryRemote::Read(uint64_t addr, void* dst, size_t bytes) {
+ // Make sure that there is no overflow.
+ uint64_t max_size;
+ if (__builtin_add_overflow(addr, bytes, &max_size)) {
+ return false;
+ }
+
size_t bytes_read = 0;
long data;
size_t align_bytes = addr & (sizeof(long) - 1);
if (align_bytes != 0) {
- if (!PtraceRead(pid_, addr & ~(sizeof(long) - 1), &data)) {
+ if (!PtraceRead(addr & ~(sizeof(long) - 1), &data)) {
return false;
}
size_t copy_bytes = std::min(sizeof(long) - align_bytes, bytes);
@@ -154,7 +167,7 @@
}
for (size_t i = 0; i < bytes / sizeof(long); i++) {
- if (!PtraceRead(pid_, addr, &data)) {
+ if (!PtraceRead(addr, &data)) {
return false;
}
memcpy(dst, &data, sizeof(long));
@@ -165,7 +178,7 @@
size_t left_over = bytes & (sizeof(long) - 1);
if (left_over) {
- if (!PtraceRead(pid_, addr, &data)) {
+ if (!PtraceRead(addr, &data)) {
return false;
}
memcpy(dst, &data, left_over);
@@ -175,7 +188,13 @@
}
bool MemoryLocal::Read(uint64_t addr, void* dst, size_t size) {
- // The process_vm_readv call does will not always work on remote
+ // Make sure that there is no overflow.
+ uint64_t max_size;
+ if (__builtin_add_overflow(addr, size, &max_size)) {
+ return false;
+ }
+
+ // The process_vm_readv call will not always work on remote
// processes, so only use it for reads from the current pid.
// Use this method to avoid crashes if an address is invalid since
// unwind data could try to access any part of the address space.
@@ -208,9 +227,29 @@
}
bool MemoryOffline::Read(uint64_t addr, void* dst, size_t size) {
- if (addr < start_ || addr + size > start_ + offset_ + size_) {
+ uint64_t max_size;
+ if (__builtin_add_overflow(addr, size, &max_size)) {
+ return false;
+ }
+
+ uint64_t real_size;
+ if (__builtin_add_overflow(start_, offset_, &real_size) ||
+ __builtin_add_overflow(real_size, size_, &real_size)) {
+ return false;
+ }
+
+ if (addr < start_ || max_size > real_size) {
return false;
}
memcpy(dst, &data_[addr + offset_ - start_ + sizeof(start_)], size);
return true;
}
+
+bool MemoryRange::Read(uint64_t addr, void* dst, size_t size) {
+ uint64_t max_read;
+ if (__builtin_add_overflow(addr, size, &max_read) || max_read > length_) {
+ return false;
+ }
+ // The check above guarantees that addr + begin_ will not overflow.
+ return memory_->Read(addr + begin_, dst, size);
+}
diff --git a/libunwindstack/Memory.h b/libunwindstack/Memory.h
index c5316a1..f9f6d56 100644
--- a/libunwindstack/Memory.h
+++ b/libunwindstack/Memory.h
@@ -17,6 +17,7 @@
#ifndef _LIBUNWINDSTACK_MEMORY_H
#define _LIBUNWINDSTACK_MEMORY_H
+#include <assert.h>
#include <stdint.h>
#include <sys/types.h>
#include <unistd.h>
@@ -33,9 +34,16 @@
virtual bool Read(uint64_t addr, void* dst, size_t size) = 0;
- inline bool Read(uint64_t addr, void* start, void* field, size_t size) {
- return Read(addr + reinterpret_cast<uintptr_t>(field) - reinterpret_cast<uintptr_t>(start),
- field, size);
+ inline bool ReadField(uint64_t addr, void* start, void* field, size_t size) {
+ if (reinterpret_cast<uintptr_t>(field) < reinterpret_cast<uintptr_t>(start)) {
+ return false;
+ }
+ uint64_t offset = reinterpret_cast<uintptr_t>(field) - reinterpret_cast<uintptr_t>(start);
+ if (__builtin_add_overflow(addr, offset, &offset)) {
+ return false;
+ }
+ // The read will check if offset + size overflows.
+ return Read(offset, field, size);
}
inline bool Read32(uint64_t addr, uint32_t* dst) {
@@ -103,6 +111,9 @@
pid_t pid() { return pid_; }
+ protected:
+ virtual bool PtraceRead(uint64_t addr, long* value);
+
private:
pid_t pid_;
};
@@ -118,15 +129,12 @@
class MemoryRange : public Memory {
public:
MemoryRange(Memory* memory, uint64_t begin, uint64_t end)
- : memory_(memory), begin_(begin), length_(end - begin_) {}
+ : memory_(memory), begin_(begin), length_(end - begin) {
+ assert(end > begin);
+ }
virtual ~MemoryRange() { delete memory_; }
- inline bool Read(uint64_t addr, void* dst, size_t size) override {
- if (addr + size <= length_) {
- return memory_->Read(addr + begin_, dst, size);
- }
- return false;
- }
+ bool Read(uint64_t addr, void* dst, size_t size) override;
private:
Memory* memory_;
diff --git a/libunwindstack/Symbols.cpp b/libunwindstack/Symbols.cpp
new file mode 100644
index 0000000..86c1233
--- /dev/null
+++ b/libunwindstack/Symbols.cpp
@@ -0,0 +1,110 @@
+/*
+ * 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 <assert.h>
+#include <elf.h>
+#include <stdint.h>
+
+#include <string>
+
+#include "Memory.h"
+#include "Symbols.h"
+
+Symbols::Symbols(uint64_t offset, uint64_t size, uint64_t entry_size, uint64_t str_offset,
+ uint64_t str_size)
+ : cur_offset_(offset),
+ offset_(offset),
+ end_(offset + size),
+ entry_size_(entry_size),
+ str_offset_(str_offset),
+ str_end_(str_offset_ + str_size) {}
+
+const Symbols::Info* Symbols::GetInfoFromCache(uint64_t addr) {
+ // Binary search the table.
+ size_t first = 0;
+ size_t last = symbols_.size();
+ while (first < last) {
+ size_t current = first + (last - first) / 2;
+ const Info* info = &symbols_[current];
+ if (addr < info->start_offset) {
+ last = current;
+ } else if (addr < info->end_offset) {
+ return info;
+ } else {
+ first = current + 1;
+ }
+ }
+ return nullptr;
+}
+
+template <typename SymType>
+bool Symbols::GetName(uint64_t addr, uint64_t load_bias, Memory* elf_memory, std::string* name,
+ uint64_t* func_offset) {
+ addr += load_bias;
+
+ if (symbols_.size() != 0) {
+ const Info* info = GetInfoFromCache(addr);
+ if (info) {
+ assert(addr >= info->start_offset && addr <= info->end_offset);
+ *func_offset = addr - info->start_offset;
+ return elf_memory->ReadString(info->str_offset, name, str_end_ - info->str_offset);
+ }
+ }
+
+ bool symbol_added = false;
+ bool return_value = false;
+ while (cur_offset_ + entry_size_ <= end_) {
+ SymType entry;
+ if (!elf_memory->Read(cur_offset_, &entry, sizeof(entry))) {
+ // Stop all processing, something looks like it is corrupted.
+ cur_offset_ = UINT64_MAX;
+ return false;
+ }
+ cur_offset_ += entry_size_;
+
+ if (entry.st_shndx != SHN_UNDEF && ELF32_ST_TYPE(entry.st_info) == STT_FUNC) {
+ // Treat st_value as virtual address.
+ uint64_t start_offset = entry.st_value;
+ if (entry.st_shndx != SHN_ABS) {
+ start_offset += load_bias;
+ }
+ uint64_t end_offset = start_offset + entry.st_size;
+
+ // Cache the value.
+ symbols_.emplace_back(start_offset, end_offset, str_offset_ + entry.st_name);
+ symbol_added = true;
+
+ if (addr >= start_offset && addr < end_offset) {
+ *func_offset = addr - start_offset;
+ uint64_t offset = str_offset_ + entry.st_name;
+ if (offset < str_end_) {
+ return_value = elf_memory->ReadString(offset, name, str_end_ - offset);
+ }
+ break;
+ }
+ }
+ }
+
+ if (symbol_added) {
+ std::sort(symbols_.begin(), symbols_.end(),
+ [](const Info& a, const Info& b) { return a.start_offset < b.start_offset; });
+ }
+ return return_value;
+}
+
+// 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*);
diff --git a/libunwindstack/Symbols.h b/libunwindstack/Symbols.h
new file mode 100644
index 0000000..3c0d033
--- /dev/null
+++ b/libunwindstack/Symbols.h
@@ -0,0 +1,64 @@
+/*
+ * 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 _LIBUNWINDSTACK_SYMBOLS_H
+#define _LIBUNWINDSTACK_SYMBOLS_H
+
+#include <stdint.h>
+
+#include <string>
+#include <vector>
+
+// Forward declaration.
+class Memory;
+
+class Symbols {
+ struct Info {
+ Info(uint64_t start_offset, uint64_t end_offset, uint64_t str_offset)
+ : start_offset(start_offset), end_offset(end_offset), str_offset(str_offset) {}
+ uint64_t start_offset;
+ uint64_t end_offset;
+ uint64_t str_offset;
+ };
+
+ public:
+ Symbols(uint64_t offset, uint64_t size, uint64_t entry_size, uint64_t str_offset,
+ uint64_t str_size);
+ virtual ~Symbols() = default;
+
+ const Info* GetInfoFromCache(uint64_t addr);
+
+ template <typename SymType>
+ bool GetName(uint64_t addr, uint64_t load_bias, Memory* elf_memory, std::string* name,
+ uint64_t* func_offset);
+
+ void ClearCache() {
+ symbols_.clear();
+ cur_offset_ = offset_;
+ }
+
+ private:
+ uint64_t cur_offset_;
+ uint64_t offset_;
+ uint64_t end_;
+ uint64_t entry_size_;
+ uint64_t str_offset_;
+ uint64_t str_end_;
+
+ std::vector<Info> symbols_;
+};
+
+#endif // _LIBUNWINDSTACK_SYMBOLS_H
diff --git a/libunwindstack/tests/DwarfMemoryTest.cpp b/libunwindstack/tests/DwarfMemoryTest.cpp
new file mode 100644
index 0000000..4877f36
--- /dev/null
+++ b/libunwindstack/tests/DwarfMemoryTest.cpp
@@ -0,0 +1,472 @@
+/*
+ * 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 <ios>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "DwarfMemory.h"
+
+#include "MemoryFake.h"
+
+class DwarfMemoryTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ memory_.Clear();
+ dwarf_mem_.reset(new DwarfMemory(&memory_));
+ }
+
+ template <typename AddressType>
+ void GetEncodedSizeTest(uint8_t value, size_t expected);
+ template <typename AddressType>
+ void ReadEncodedValue_omit();
+ template <typename AddressType>
+ void ReadEncodedValue_leb128();
+ template <typename AddressType>
+ void ReadEncodedValue_data1();
+ template <typename AddressType>
+ void ReadEncodedValue_data2();
+ template <typename AddressType>
+ void ReadEncodedValue_data4();
+ template <typename AddressType>
+ void ReadEncodedValue_data8();
+ template <typename AddressType>
+ void ReadEncodedValue_non_zero_adjust();
+ template <typename AddressType>
+ void ReadEncodedValue_overflow();
+
+ MemoryFake memory_;
+ std::unique_ptr<DwarfMemory> dwarf_mem_;
+};
+
+TEST_F(DwarfMemoryTest, ReadBytes) {
+ memory_.SetMemory(0, std::vector<uint8_t>{0x10, 0x18, 0xff, 0xfe});
+
+ uint8_t byte;
+ ASSERT_TRUE(dwarf_mem_->ReadBytes(&byte, 1));
+ ASSERT_EQ(0x10U, byte);
+ ASSERT_TRUE(dwarf_mem_->ReadBytes(&byte, 1));
+ ASSERT_EQ(0x18U, byte);
+ ASSERT_TRUE(dwarf_mem_->ReadBytes(&byte, 1));
+ ASSERT_EQ(0xffU, byte);
+ ASSERT_TRUE(dwarf_mem_->ReadBytes(&byte, 1));
+ ASSERT_EQ(0xfeU, byte);
+ ASSERT_EQ(4U, dwarf_mem_->cur_offset());
+
+ dwarf_mem_->set_cur_offset(2);
+ ASSERT_TRUE(dwarf_mem_->ReadBytes(&byte, 1));
+ ASSERT_EQ(0xffU, byte);
+ ASSERT_EQ(3U, dwarf_mem_->cur_offset());
+}
+
+TEST_F(DwarfMemoryTest, ReadSigned_check) {
+ uint64_t value;
+
+ // Signed 8 byte reads.
+ memory_.SetData8(0, static_cast<uint8_t>(-10));
+ memory_.SetData8(1, 200);
+ ASSERT_TRUE(dwarf_mem_->ReadSigned<int8_t>(&value));
+ ASSERT_EQ(static_cast<int8_t>(-10), static_cast<int8_t>(value));
+ ASSERT_TRUE(dwarf_mem_->ReadSigned<int8_t>(&value));
+ ASSERT_EQ(static_cast<int8_t>(200), static_cast<int8_t>(value));
+
+ // Signed 16 byte reads.
+ memory_.SetData16(0x10, static_cast<uint16_t>(-1000));
+ memory_.SetData16(0x12, 50100);
+ dwarf_mem_->set_cur_offset(0x10);
+ ASSERT_TRUE(dwarf_mem_->ReadSigned<int16_t>(&value));
+ ASSERT_EQ(static_cast<int16_t>(-1000), static_cast<int16_t>(value));
+ ASSERT_TRUE(dwarf_mem_->ReadSigned<int16_t>(&value));
+ ASSERT_EQ(static_cast<int16_t>(50100), static_cast<int16_t>(value));
+
+ // Signed 32 byte reads.
+ memory_.SetData32(0x100, static_cast<uint32_t>(-1000000000));
+ memory_.SetData32(0x104, 3000000000);
+ dwarf_mem_->set_cur_offset(0x100);
+ ASSERT_TRUE(dwarf_mem_->ReadSigned<int32_t>(&value));
+ ASSERT_EQ(static_cast<int32_t>(-1000000000), static_cast<int32_t>(value));
+ ASSERT_TRUE(dwarf_mem_->ReadSigned<int32_t>(&value));
+ ASSERT_EQ(static_cast<int32_t>(3000000000), static_cast<int32_t>(value));
+
+ // Signed 64 byte reads.
+ memory_.SetData64(0x200, static_cast<uint64_t>(-2000000000000LL));
+ memory_.SetData64(0x208, 5000000000000LL);
+ dwarf_mem_->set_cur_offset(0x200);
+ ASSERT_TRUE(dwarf_mem_->ReadSigned<int64_t>(&value));
+ ASSERT_EQ(static_cast<int64_t>(-2000000000000), static_cast<int64_t>(value));
+ ASSERT_TRUE(dwarf_mem_->ReadSigned<int64_t>(&value));
+ ASSERT_EQ(static_cast<int64_t>(5000000000000), static_cast<int64_t>(value));
+}
+
+TEST_F(DwarfMemoryTest, ReadULEB128) {
+ memory_.SetMemory(0, std::vector<uint8_t>{0x01, 0x80, 0x24, 0xff, 0xc3, 0xff, 0x7f});
+
+ uint64_t value;
+ ASSERT_TRUE(dwarf_mem_->ReadULEB128(&value));
+ ASSERT_EQ(1U, dwarf_mem_->cur_offset());
+ ASSERT_EQ(1U, value);
+
+ ASSERT_TRUE(dwarf_mem_->ReadULEB128(&value));
+ ASSERT_EQ(3U, dwarf_mem_->cur_offset());
+ ASSERT_EQ(0x1200U, value);
+
+ ASSERT_TRUE(dwarf_mem_->ReadULEB128(&value));
+ ASSERT_EQ(7U, dwarf_mem_->cur_offset());
+ ASSERT_EQ(0xfffe1ffU, value);
+}
+
+TEST_F(DwarfMemoryTest, ReadSLEB128) {
+ memory_.SetMemory(0, std::vector<uint8_t>{0x06, 0x40, 0x82, 0x34, 0x89, 0x64, 0xf9, 0xc3, 0x8f,
+ 0x2f, 0xbf, 0xc3, 0xf7, 0x5f});
+
+ int64_t value;
+ ASSERT_TRUE(dwarf_mem_->ReadSLEB128(&value));
+ ASSERT_EQ(1U, dwarf_mem_->cur_offset());
+ ASSERT_EQ(6U, value);
+
+ ASSERT_TRUE(dwarf_mem_->ReadSLEB128(&value));
+ ASSERT_EQ(2U, dwarf_mem_->cur_offset());
+ ASSERT_EQ(0xffffffffffffffc0ULL, static_cast<uint64_t>(value));
+
+ ASSERT_TRUE(dwarf_mem_->ReadSLEB128(&value));
+ ASSERT_EQ(4U, dwarf_mem_->cur_offset());
+ ASSERT_EQ(0x1a02U, value);
+
+ ASSERT_TRUE(dwarf_mem_->ReadSLEB128(&value));
+ ASSERT_EQ(6U, dwarf_mem_->cur_offset());
+ ASSERT_EQ(0xfffffffffffff209ULL, static_cast<uint64_t>(value));
+
+ ASSERT_TRUE(dwarf_mem_->ReadSLEB128(&value));
+ ASSERT_EQ(10U, dwarf_mem_->cur_offset());
+ ASSERT_EQ(0x5e3e1f9U, value);
+
+ ASSERT_TRUE(dwarf_mem_->ReadSLEB128(&value));
+ ASSERT_EQ(14U, dwarf_mem_->cur_offset());
+ ASSERT_EQ(0xfffffffffbfde1bfULL, static_cast<uint64_t>(value));
+}
+
+template <typename AddressType>
+void DwarfMemoryTest::GetEncodedSizeTest(uint8_t value, size_t expected) {
+ for (size_t i = 0; i < 16; i++) {
+ uint8_t encoding = (i << 4) | value;
+ ASSERT_EQ(expected, dwarf_mem_->GetEncodedSize<AddressType>(encoding))
+ << "encoding 0x" << std::hex << static_cast<uint32_t>(encoding) << " test value 0x"
+ << static_cast<size_t>(value);
+ }
+}
+
+TEST_F(DwarfMemoryTest, GetEncodedSize_absptr_uint32_t) {
+ GetEncodedSizeTest<uint32_t>(0, sizeof(uint32_t));
+}
+
+TEST_F(DwarfMemoryTest, GetEncodedSize_absptr_uint64_t) {
+ GetEncodedSizeTest<uint64_t>(0, sizeof(uint64_t));
+}
+
+TEST_F(DwarfMemoryTest, GetEncodedSize_data1) {
+ // udata1
+ GetEncodedSizeTest<uint32_t>(0x0d, 1);
+ GetEncodedSizeTest<uint64_t>(0x0d, 1);
+
+ // sdata1
+ GetEncodedSizeTest<uint32_t>(0x0e, 1);
+ GetEncodedSizeTest<uint64_t>(0x0e, 1);
+}
+
+TEST_F(DwarfMemoryTest, GetEncodedSize_data2) {
+ // udata2
+ GetEncodedSizeTest<uint32_t>(0x02, 2);
+ GetEncodedSizeTest<uint64_t>(0x02, 2);
+
+ // sdata2
+ GetEncodedSizeTest<uint32_t>(0x0a, 2);
+ GetEncodedSizeTest<uint64_t>(0x0a, 2);
+}
+
+TEST_F(DwarfMemoryTest, GetEncodedSize_data4) {
+ // udata4
+ GetEncodedSizeTest<uint32_t>(0x03, 4);
+ GetEncodedSizeTest<uint64_t>(0x03, 4);
+
+ // sdata4
+ GetEncodedSizeTest<uint32_t>(0x0b, 4);
+ GetEncodedSizeTest<uint64_t>(0x0b, 4);
+}
+
+TEST_F(DwarfMemoryTest, GetEncodedSize_data8) {
+ // udata8
+ GetEncodedSizeTest<uint32_t>(0x04, 8);
+ GetEncodedSizeTest<uint64_t>(0x04, 8);
+
+ // sdata8
+ GetEncodedSizeTest<uint32_t>(0x0c, 8);
+ GetEncodedSizeTest<uint64_t>(0x0c, 8);
+}
+
+TEST_F(DwarfMemoryTest, GetEncodedSize_unknown) {
+ GetEncodedSizeTest<uint32_t>(0x01, 0);
+ GetEncodedSizeTest<uint64_t>(0x01, 0);
+
+ GetEncodedSizeTest<uint32_t>(0x09, 0);
+ GetEncodedSizeTest<uint64_t>(0x09, 0);
+
+ GetEncodedSizeTest<uint32_t>(0x0f, 0);
+ GetEncodedSizeTest<uint64_t>(0x0f, 0);
+}
+
+template <typename AddressType>
+void DwarfMemoryTest::ReadEncodedValue_omit() {
+ uint64_t value = 123;
+ ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<AddressType>(0xff, &value));
+ ASSERT_EQ(0U, value);
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_omit_uint32_t) { ReadEncodedValue_omit<uint32_t>(); }
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_omit_uint64_t) { ReadEncodedValue_omit<uint64_t>(); }
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_absptr_uint32_t) {
+ uint64_t value = 100;
+ ASSERT_FALSE(dwarf_mem_->ReadEncodedValue<uint32_t>(0x00, &value));
+
+ memory_.SetData32(0, 0x12345678);
+
+ ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<uint32_t>(0x00, &value));
+ ASSERT_EQ(4U, dwarf_mem_->cur_offset());
+ ASSERT_EQ(0x12345678U, value);
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_absptr_uint64_t) {
+ uint64_t value = 100;
+ ASSERT_FALSE(dwarf_mem_->ReadEncodedValue<uint64_t>(0x00, &value));
+
+ memory_.SetData64(0, 0x12345678f1f2f3f4ULL);
+
+ ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<uint64_t>(0x00, &value));
+ ASSERT_EQ(8U, dwarf_mem_->cur_offset());
+ ASSERT_EQ(0x12345678f1f2f3f4ULL, value);
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_aligned_uint32_t) {
+ uint64_t value = 100;
+ dwarf_mem_->set_cur_offset(1);
+ ASSERT_FALSE(dwarf_mem_->ReadEncodedValue<uint32_t>(0x50, &value));
+
+ memory_.SetData32(4, 0x12345678);
+
+ ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<uint32_t>(0x50, &value));
+ ASSERT_EQ(8U, dwarf_mem_->cur_offset());
+ ASSERT_EQ(0x12345678U, value);
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_aligned_uint64_t) {
+ uint64_t value = 100;
+ dwarf_mem_->set_cur_offset(1);
+ ASSERT_FALSE(dwarf_mem_->ReadEncodedValue<uint64_t>(0x50, &value));
+
+ memory_.SetData64(8, 0x12345678f1f2f3f4ULL);
+
+ ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<uint64_t>(0x50, &value));
+ ASSERT_EQ(16U, dwarf_mem_->cur_offset());
+ ASSERT_EQ(0x12345678f1f2f3f4ULL, value);
+}
+
+template <typename AddressType>
+void DwarfMemoryTest::ReadEncodedValue_leb128() {
+ memory_.SetMemory(0, std::vector<uint8_t>{0x80, 0x42});
+
+ uint64_t value = 100;
+ // uleb128
+ ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<AddressType>(0x01, &value));
+ ASSERT_EQ(0x2100U, value);
+
+ dwarf_mem_->set_cur_offset(0);
+ // sleb128
+ ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<AddressType>(0x09, &value));
+ ASSERT_EQ(0xffffffffffffe100ULL, value);
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_leb128_uint32_t) { ReadEncodedValue_leb128<uint32_t>(); }
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_leb128_uint64_t) { ReadEncodedValue_leb128<uint64_t>(); }
+
+template <typename AddressType>
+void DwarfMemoryTest::ReadEncodedValue_data1() {
+ memory_.SetData8(0, 0xe0);
+
+ uint64_t value = 0;
+ ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<AddressType>(0x0d, &value));
+ ASSERT_EQ(0xe0U, value);
+
+ dwarf_mem_->set_cur_offset(0);
+ ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<AddressType>(0x0e, &value));
+ ASSERT_EQ(0xffffffffffffffe0ULL, value);
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_data1_uint32_t) { ReadEncodedValue_data1<uint32_t>(); }
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_data1_uint64_t) { ReadEncodedValue_data1<uint64_t>(); }
+
+template <typename AddressType>
+void DwarfMemoryTest::ReadEncodedValue_data2() {
+ memory_.SetData16(0, 0xe000);
+
+ uint64_t value = 0;
+ ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<AddressType>(0x02, &value));
+ ASSERT_EQ(0xe000U, value);
+
+ dwarf_mem_->set_cur_offset(0);
+ ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<AddressType>(0x0a, &value));
+ ASSERT_EQ(0xffffffffffffe000ULL, value);
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_data2_uint32_t) { ReadEncodedValue_data2<uint32_t>(); }
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_data2_uint64_t) { ReadEncodedValue_data2<uint64_t>(); }
+
+template <typename AddressType>
+void DwarfMemoryTest::ReadEncodedValue_data4() {
+ memory_.SetData32(0, 0xe0000000);
+
+ uint64_t value = 0;
+ ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<AddressType>(0x03, &value));
+ ASSERT_EQ(0xe0000000U, value);
+
+ dwarf_mem_->set_cur_offset(0);
+ ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<AddressType>(0x0b, &value));
+ ASSERT_EQ(0xffffffffe0000000ULL, value);
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_data4_uint32_t) { ReadEncodedValue_data4<uint32_t>(); }
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_data4_uint64_t) { ReadEncodedValue_data4<uint64_t>(); }
+
+template <typename AddressType>
+void DwarfMemoryTest::ReadEncodedValue_data8() {
+ memory_.SetData64(0, 0xe000000000000000ULL);
+
+ uint64_t value = 0;
+ ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<AddressType>(0x04, &value));
+ ASSERT_EQ(0xe000000000000000ULL, value);
+
+ dwarf_mem_->set_cur_offset(0);
+ ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<AddressType>(0x0c, &value));
+ ASSERT_EQ(0xe000000000000000ULL, value);
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_data8_uint32_t) { ReadEncodedValue_data8<uint32_t>(); }
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_data8_uint64_t) { ReadEncodedValue_data8<uint64_t>(); }
+
+template <typename AddressType>
+void DwarfMemoryTest::ReadEncodedValue_non_zero_adjust() {
+ memory_.SetData64(0, 0xe000000000000000ULL);
+
+ uint64_t value = 0;
+ dwarf_mem_->set_pc_offset(0x2000);
+ ASSERT_TRUE(dwarf_mem_->ReadEncodedValue<AddressType>(0x14, &value));
+ ASSERT_EQ(0xe000000000002000ULL, value);
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_non_zero_adjust_uint32_t) {
+ ReadEncodedValue_non_zero_adjust<uint32_t>();
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_non_zero_adjust_uint64_t) {
+ ReadEncodedValue_non_zero_adjust<uint64_t>();
+}
+
+template <typename AddressType>
+void DwarfMemoryTest::ReadEncodedValue_overflow() {
+ memory_.SetData64(0, 0);
+
+ uint64_t value = 0;
+ dwarf_mem_->set_cur_offset(UINT64_MAX);
+ ASSERT_FALSE(dwarf_mem_->ReadEncodedValue<AddressType>(0x50, &value));
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_overflow_uint32_t) {
+ ReadEncodedValue_overflow<uint32_t>();
+}
+
+TEST_F(DwarfMemoryTest, ReadEncodedValue_overflow_uint64_t) {
+ ReadEncodedValue_overflow<uint64_t>();
+}
+
+TEST_F(DwarfMemoryTest, AdjustEncodedValue_absptr) {
+ uint64_t value = 0x1234;
+ ASSERT_TRUE(dwarf_mem_->AdjustEncodedValue(0x00, &value));
+ ASSERT_EQ(0x1234U, value);
+}
+
+TEST_F(DwarfMemoryTest, AdjustEncodedValue_pcrel) {
+ uint64_t value = 0x1234;
+ ASSERT_FALSE(dwarf_mem_->AdjustEncodedValue(0x10, &value));
+
+ dwarf_mem_->set_pc_offset(0x2000);
+ ASSERT_TRUE(dwarf_mem_->AdjustEncodedValue(0x10, &value));
+ ASSERT_EQ(0x3234U, value);
+
+ dwarf_mem_->set_pc_offset(static_cast<uint64_t>(-4));
+ value = 0x1234;
+ ASSERT_TRUE(dwarf_mem_->AdjustEncodedValue(0x10, &value));
+ ASSERT_EQ(0x1230U, value);
+}
+
+TEST_F(DwarfMemoryTest, AdjustEncodedValue_textrel) {
+ uint64_t value = 0x8234;
+ ASSERT_FALSE(dwarf_mem_->AdjustEncodedValue(0x20, &value));
+
+ dwarf_mem_->set_text_offset(0x1000);
+ ASSERT_TRUE(dwarf_mem_->AdjustEncodedValue(0x20, &value));
+ ASSERT_EQ(0x9234U, value);
+
+ dwarf_mem_->set_text_offset(static_cast<uint64_t>(-16));
+ value = 0x8234;
+ ASSERT_TRUE(dwarf_mem_->AdjustEncodedValue(0x20, &value));
+ ASSERT_EQ(0x8224U, value);
+}
+
+TEST_F(DwarfMemoryTest, AdjustEncodedValue_datarel) {
+ uint64_t value = 0xb234;
+ ASSERT_FALSE(dwarf_mem_->AdjustEncodedValue(0x30, &value));
+
+ dwarf_mem_->set_data_offset(0x1200);
+ ASSERT_TRUE(dwarf_mem_->AdjustEncodedValue(0x30, &value));
+ ASSERT_EQ(0xc434U, value);
+
+ dwarf_mem_->set_data_offset(static_cast<uint64_t>(-256));
+ value = 0xb234;
+ ASSERT_TRUE(dwarf_mem_->AdjustEncodedValue(0x30, &value));
+ ASSERT_EQ(0xb134U, value);
+}
+
+TEST_F(DwarfMemoryTest, AdjustEncodedValue_funcrel) {
+ uint64_t value = 0x15234;
+ ASSERT_FALSE(dwarf_mem_->AdjustEncodedValue(0x40, &value));
+
+ dwarf_mem_->set_func_offset(0x60000);
+ ASSERT_TRUE(dwarf_mem_->AdjustEncodedValue(0x40, &value));
+ ASSERT_EQ(0x75234U, value);
+
+ dwarf_mem_->set_func_offset(static_cast<uint64_t>(-4096));
+ value = 0x15234;
+ ASSERT_TRUE(dwarf_mem_->AdjustEncodedValue(0x40, &value));
+ ASSERT_EQ(0x14234U, value);
+}
diff --git a/libunwindstack/tests/DwarfOpLogTest.cpp b/libunwindstack/tests/DwarfOpLogTest.cpp
new file mode 100644
index 0000000..d18aad0
--- /dev/null
+++ b/libunwindstack/tests/DwarfOpLogTest.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+
+#include <ios>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "DwarfError.h"
+#include "DwarfMemory.h"
+#include "DwarfOp.h"
+#include "Log.h"
+#include "Regs.h"
+
+#include "MemoryFake.h"
+
+template <typename TypeParam>
+class DwarfOpLogTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ op_memory_.Clear();
+ regular_memory_.Clear();
+ mem_.reset(new DwarfMemory(&op_memory_));
+ op_.reset(new DwarfOp<TypeParam>(mem_.get(), ®ular_memory_));
+ }
+
+ MemoryFake op_memory_;
+ MemoryFake regular_memory_;
+
+ std::unique_ptr<DwarfMemory> mem_;
+ std::unique_ptr<DwarfOp<TypeParam>> op_;
+};
+TYPED_TEST_CASE_P(DwarfOpLogTest);
+
+TYPED_TEST_P(DwarfOpLogTest, multiple_ops) {
+ // Multi operation opcodes.
+ std::vector<uint8_t> opcode_buffer = {
+ 0x0a, 0x20, 0x10, 0x08, 0x03, 0x12, 0x27,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ std::vector<std::string> lines;
+ this->op_->GetLogInfo(0, opcode_buffer.size(), &lines);
+ std::vector<std::string> expected{
+ "DW_OP_const2u 4128", "Raw Data: 0x0a 0x20 0x10", "DW_OP_const1u 3", "Raw Data: 0x08 0x03",
+ "DW_OP_dup", "Raw Data: 0x12", "DW_OP_xor", "Raw Data: 0x27"};
+ ASSERT_EQ(expected, lines);
+}
+
+REGISTER_TYPED_TEST_CASE_P(DwarfOpLogTest, multiple_ops);
+
+typedef ::testing::Types<uint32_t, uint64_t> DwarfOpLogTestTypes;
+INSTANTIATE_TYPED_TEST_CASE_P(, DwarfOpLogTest, DwarfOpLogTestTypes);
diff --git a/libunwindstack/tests/DwarfOpTest.cpp b/libunwindstack/tests/DwarfOpTest.cpp
new file mode 100644
index 0000000..520c545
--- /dev/null
+++ b/libunwindstack/tests/DwarfOpTest.cpp
@@ -0,0 +1,1593 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdint.h>
+
+#include <ios>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "DwarfError.h"
+#include "DwarfMemory.h"
+#include "DwarfOp.h"
+#include "Log.h"
+#include "Regs.h"
+
+#include "MemoryFake.h"
+
+template <typename TypeParam>
+class RegsFake : public RegsTmpl<TypeParam> {
+ public:
+ RegsFake(uint16_t total_regs, uint16_t sp_reg)
+ : RegsTmpl<TypeParam>(total_regs, sp_reg, Regs::Location(Regs::LOCATION_UNKNOWN, 0)) {}
+ virtual ~RegsFake() = default;
+
+ uint64_t GetRelPc(Elf*, const MapInfo*) override { return 0; }
+ uint64_t GetAdjustedPc(uint64_t, Elf*) override { return 0; }
+ bool GetReturnAddressFromDefault(Memory*, uint64_t*) { return false; }
+};
+
+template <typename TypeParam>
+class DwarfOpTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ op_memory_.Clear();
+ regular_memory_.Clear();
+ mem_.reset(new DwarfMemory(&op_memory_));
+ op_.reset(new DwarfOp<TypeParam>(mem_.get(), ®ular_memory_));
+ }
+
+ MemoryFake op_memory_;
+ MemoryFake regular_memory_;
+
+ std::unique_ptr<DwarfMemory> mem_;
+ std::unique_ptr<DwarfOp<TypeParam>> op_;
+};
+TYPED_TEST_CASE_P(DwarfOpTest);
+
+TYPED_TEST_P(DwarfOpTest, decode) {
+ // Memory error.
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_MEMORY_INVALID, this->op_->last_error());
+
+ // No error.
+ this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x96});
+ this->mem_->set_cur_offset(0);
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_NONE, this->op_->last_error());
+ ASSERT_EQ(0x96U, this->op_->cur_op());
+ ASSERT_EQ(1U, this->mem_->cur_offset());
+}
+
+TYPED_TEST_P(DwarfOpTest, eval) {
+ // Memory error.
+ ASSERT_FALSE(this->op_->Eval(0, 2, DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_MEMORY_INVALID, this->op_->last_error());
+
+ // Register set.
+ // Do this first, to verify that subsequent calls reset the value.
+ this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x50});
+ ASSERT_TRUE(this->op_->Eval(0, 1, DWARF_VERSION_MAX));
+ ASSERT_TRUE(this->op_->is_register());
+ ASSERT_EQ(1U, this->mem_->cur_offset());
+ ASSERT_EQ(1U, this->op_->StackSize());
+
+ // Multi operation opcodes.
+ std::vector<uint8_t> opcode_buffer = {
+ 0x08, 0x04, 0x08, 0x03, 0x08, 0x02, 0x08, 0x01,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_TRUE(this->op_->Eval(0, 8, DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_NONE, this->op_->last_error());
+ ASSERT_FALSE(this->op_->is_register());
+ ASSERT_EQ(8U, this->mem_->cur_offset());
+ ASSERT_EQ(4U, this->op_->StackSize());
+ ASSERT_EQ(1U, this->op_->StackAt(0));
+ ASSERT_EQ(2U, this->op_->StackAt(1));
+ ASSERT_EQ(3U, this->op_->StackAt(2));
+ ASSERT_EQ(4U, this->op_->StackAt(3));
+
+ // Infinite loop.
+ this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x2f, 0xfd, 0xff});
+ ASSERT_FALSE(this->op_->Eval(0, 4, DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_TOO_MANY_ITERATIONS, this->op_->last_error());
+ ASSERT_FALSE(this->op_->is_register());
+ ASSERT_EQ(0U, this->op_->StackSize());
+}
+
+TYPED_TEST_P(DwarfOpTest, illegal_opcode) {
+ // Fill the buffer with all of the illegal opcodes.
+ std::vector<uint8_t> opcode_buffer = {0x00, 0x01, 0x02, 0x04, 0x05, 0x07};
+ for (size_t opcode = 0xa0; opcode < 256; opcode++) {
+ opcode_buffer.push_back(opcode);
+ }
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ for (size_t i = 0; i < opcode_buffer.size(); i++) {
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->last_error());
+ ASSERT_EQ(opcode_buffer[i], this->op_->cur_op());
+ }
+}
+
+TYPED_TEST_P(DwarfOpTest, illegal_in_version3) {
+ std::vector<uint8_t> opcode_buffer = {0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d};
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ for (size_t i = 0; i < opcode_buffer.size(); i++) {
+ ASSERT_FALSE(this->op_->Decode(2));
+ ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->last_error());
+ ASSERT_EQ(opcode_buffer[i], this->op_->cur_op());
+ }
+}
+
+TYPED_TEST_P(DwarfOpTest, illegal_in_version4) {
+ std::vector<uint8_t> opcode_buffer = {0x9e, 0x9f};
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ for (size_t i = 0; i < opcode_buffer.size(); i++) {
+ ASSERT_FALSE(this->op_->Decode(3));
+ ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->last_error());
+ ASSERT_EQ(opcode_buffer[i], this->op_->cur_op());
+ }
+}
+
+TYPED_TEST_P(DwarfOpTest, not_implemented) {
+ std::vector<uint8_t> opcode_buffer = {
+ // Push values so that any not implemented ops will return the right error.
+ 0x08, 0x03, 0x08, 0x02, 0x08, 0x01,
+ // xderef
+ 0x18,
+ // fbreg
+ 0x91, 0x01,
+ // piece
+ 0x93, 0x01,
+ // xderef_size
+ 0x95, 0x01,
+ // push_object_address
+ 0x97,
+ // call2
+ 0x98, 0x01, 0x02,
+ // call4
+ 0x99, 0x01, 0x02, 0x03, 0x04,
+ // call_ref
+ 0x9a,
+ // form_tls_address
+ 0x9b,
+ // call_frame_cfa
+ 0x9c,
+ // bit_piece
+ 0x9d, 0x01, 0x01,
+ // implicit_value
+ 0x9e, 0x01,
+ // stack_value
+ 0x9f,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ // Push the stack values.
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+
+ while (this->mem_->cur_offset() < opcode_buffer.size()) {
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_NOT_IMPLEMENTED, this->op_->last_error());
+ }
+}
+
+TYPED_TEST_P(DwarfOpTest, op_addr) {
+ std::vector<uint8_t> opcode_buffer = {0x03, 0x12, 0x23, 0x34, 0x45};
+ if (sizeof(TypeParam) == 8) {
+ opcode_buffer.push_back(0x56);
+ opcode_buffer.push_back(0x67);
+ opcode_buffer.push_back(0x78);
+ opcode_buffer.push_back(0x89);
+ }
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x03, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ if (sizeof(TypeParam) == 4) {
+ ASSERT_EQ(0x45342312U, this->op_->StackAt(0));
+ } else {
+ ASSERT_EQ(0x8978675645342312UL, this->op_->StackAt(0));
+ }
+}
+
+TYPED_TEST_P(DwarfOpTest, op_deref) {
+ std::vector<uint8_t> opcode_buffer = {
+ // Try a dereference with nothing on the stack.
+ 0x06,
+ // Add an address, then dereference.
+ 0x0a, 0x10, 0x20, 0x06,
+ // Now do another dereference that should fail in memory.
+ 0x06,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+ TypeParam value = 0x12345678;
+ this->regular_memory_.SetMemory(0x2010, &value, sizeof(value));
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x06, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(value, this->op_->StackAt(0));
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_MEMORY_INVALID, this->op_->last_error());
+}
+
+TYPED_TEST_P(DwarfOpTest, op_deref_size) {
+ this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x94});
+ TypeParam value = 0x12345678;
+ this->regular_memory_.SetMemory(0x2010, &value, sizeof(value));
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ // Read all byte sizes up to the sizeof the type.
+ for (size_t i = 1; i < sizeof(TypeParam); i++) {
+ this->op_memory_.SetMemory(
+ 0, std::vector<uint8_t>{0x0a, 0x10, 0x20, 0x94, static_cast<uint8_t>(i)});
+ ASSERT_TRUE(this->op_->Eval(0, 5, DWARF_VERSION_MAX)) << "Failed at size " << i;
+ ASSERT_EQ(1U, this->op_->StackSize()) << "Failed at size " << i;
+ ASSERT_EQ(0x94, this->op_->cur_op()) << "Failed at size " << i;
+ TypeParam expected_value = 0;
+ memcpy(&expected_value, &value, i);
+ ASSERT_EQ(expected_value, this->op_->StackAt(0)) << "Failed at size " << i;
+ }
+
+ // Zero byte read.
+ this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x0a, 0x10, 0x20, 0x94, 0x00});
+ ASSERT_FALSE(this->op_->Eval(0, 5, DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->last_error());
+
+ // Read too many bytes.
+ this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x0a, 0x10, 0x20, 0x94, sizeof(TypeParam) + 1});
+ ASSERT_FALSE(this->op_->Eval(0, 5, DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->last_error());
+
+ // Force bad memory read.
+ this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x0a, 0x10, 0x40, 0x94, 0x01});
+ ASSERT_FALSE(this->op_->Eval(0, 5, DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_MEMORY_INVALID, this->op_->last_error());
+}
+
+TYPED_TEST_P(DwarfOpTest, const_unsigned) {
+ std::vector<uint8_t> opcode_buffer = {
+ // const1u
+ 0x08, 0x12, 0x08, 0xff,
+ // const2u
+ 0x0a, 0x45, 0x12, 0x0a, 0x00, 0xff,
+ // const4u
+ 0x0c, 0x12, 0x23, 0x34, 0x45, 0x0c, 0x03, 0x02, 0x01, 0xff,
+ // const8u
+ 0x0e, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x0e, 0x87, 0x98, 0xa9, 0xba, 0xcb,
+ 0xdc, 0xed, 0xfe,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ // const1u
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x08, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(0x12U, this->op_->StackAt(0));
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x08, this->op_->cur_op());
+ ASSERT_EQ(2U, this->op_->StackSize());
+ ASSERT_EQ(0xffU, this->op_->StackAt(0));
+
+ // const2u
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x0a, this->op_->cur_op());
+ ASSERT_EQ(3U, this->op_->StackSize());
+ ASSERT_EQ(0x1245U, this->op_->StackAt(0));
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x0a, this->op_->cur_op());
+ ASSERT_EQ(4U, this->op_->StackSize());
+ ASSERT_EQ(0xff00U, this->op_->StackAt(0));
+
+ // const4u
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x0c, this->op_->cur_op());
+ ASSERT_EQ(5U, this->op_->StackSize());
+ ASSERT_EQ(0x45342312U, this->op_->StackAt(0));
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x0c, this->op_->cur_op());
+ ASSERT_EQ(6U, this->op_->StackSize());
+ ASSERT_EQ(0xff010203U, this->op_->StackAt(0));
+
+ // const8u
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x0e, this->op_->cur_op());
+ ASSERT_EQ(7U, this->op_->StackSize());
+ if (sizeof(TypeParam) == 4) {
+ ASSERT_EQ(0x05060708U, this->op_->StackAt(0));
+ } else {
+ ASSERT_EQ(0x0102030405060708ULL, this->op_->StackAt(0));
+ }
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x0e, this->op_->cur_op());
+ ASSERT_EQ(8U, this->op_->StackSize());
+ if (sizeof(TypeParam) == 4) {
+ ASSERT_EQ(0xbaa99887UL, this->op_->StackAt(0));
+ } else {
+ ASSERT_EQ(0xfeeddccbbaa99887ULL, this->op_->StackAt(0));
+ }
+}
+
+TYPED_TEST_P(DwarfOpTest, const_signed) {
+ std::vector<uint8_t> opcode_buffer = {
+ // const1s
+ 0x09, 0x12, 0x09, 0xff,
+ // const2s
+ 0x0b, 0x21, 0x32, 0x0b, 0x08, 0xff,
+ // const4s
+ 0x0d, 0x45, 0x34, 0x23, 0x12, 0x0d, 0x01, 0x02, 0x03, 0xff,
+ // const8s
+ 0x0f, 0x89, 0x78, 0x67, 0x56, 0x45, 0x34, 0x23, 0x12, 0x0f, 0x04, 0x03, 0x02, 0x01, 0xef,
+ 0xef, 0xef, 0xff,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ // const1s
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x09, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(0x12U, this->op_->StackAt(0));
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x09, this->op_->cur_op());
+ ASSERT_EQ(2U, this->op_->StackSize());
+ ASSERT_EQ(static_cast<TypeParam>(-1), this->op_->StackAt(0));
+
+ // const2s
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x0b, this->op_->cur_op());
+ ASSERT_EQ(3U, this->op_->StackSize());
+ ASSERT_EQ(0x3221U, this->op_->StackAt(0));
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x0b, this->op_->cur_op());
+ ASSERT_EQ(4U, this->op_->StackSize());
+ ASSERT_EQ(static_cast<TypeParam>(-248), this->op_->StackAt(0));
+
+ // const4s
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x0d, this->op_->cur_op());
+ ASSERT_EQ(5U, this->op_->StackSize());
+ ASSERT_EQ(0x12233445U, this->op_->StackAt(0));
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x0d, this->op_->cur_op());
+ ASSERT_EQ(6U, this->op_->StackSize());
+ ASSERT_EQ(static_cast<TypeParam>(-16580095), this->op_->StackAt(0));
+
+ // const8s
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x0f, this->op_->cur_op());
+ ASSERT_EQ(7U, this->op_->StackSize());
+ if (sizeof(TypeParam) == 4) {
+ ASSERT_EQ(0x56677889ULL, this->op_->StackAt(0));
+ } else {
+ ASSERT_EQ(0x1223344556677889ULL, this->op_->StackAt(0));
+ }
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x0f, this->op_->cur_op());
+ ASSERT_EQ(8U, this->op_->StackSize());
+ if (sizeof(TypeParam) == 4) {
+ ASSERT_EQ(0x01020304U, this->op_->StackAt(0));
+ } else {
+ ASSERT_EQ(static_cast<TypeParam>(-4521264810949884LL), this->op_->StackAt(0));
+ }
+}
+
+TYPED_TEST_P(DwarfOpTest, const_uleb) {
+ std::vector<uint8_t> opcode_buffer = {
+ // Single byte ULEB128
+ 0x10, 0x22, 0x10, 0x7f,
+ // Multi byte ULEB128
+ 0x10, 0xa2, 0x22, 0x10, 0xa2, 0x74, 0x10, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88,
+ 0x09, 0x10, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x79,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ // Single byte ULEB128
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x10, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(0x22U, this->op_->StackAt(0));
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x10, this->op_->cur_op());
+ ASSERT_EQ(2U, this->op_->StackSize());
+ ASSERT_EQ(0x7fU, this->op_->StackAt(0));
+
+ // Multi byte ULEB128
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x10, this->op_->cur_op());
+ ASSERT_EQ(3U, this->op_->StackSize());
+ ASSERT_EQ(0x1122U, this->op_->StackAt(0));
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x10, this->op_->cur_op());
+ ASSERT_EQ(4U, this->op_->StackSize());
+ ASSERT_EQ(0x3a22U, this->op_->StackAt(0));
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x10, this->op_->cur_op());
+ ASSERT_EQ(5U, this->op_->StackSize());
+ if (sizeof(TypeParam) == 4) {
+ ASSERT_EQ(0x5080c101U, this->op_->StackAt(0));
+ } else {
+ ASSERT_EQ(0x9101c305080c101ULL, this->op_->StackAt(0));
+ }
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x10, this->op_->cur_op());
+ ASSERT_EQ(6U, this->op_->StackSize());
+ if (sizeof(TypeParam) == 4) {
+ ASSERT_EQ(0x5080c101U, this->op_->StackAt(0));
+ } else {
+ ASSERT_EQ(0x79101c305080c101ULL, this->op_->StackAt(0));
+ }
+}
+
+TYPED_TEST_P(DwarfOpTest, const_sleb) {
+ std::vector<uint8_t> opcode_buffer = {
+ // Single byte SLEB128
+ 0x11, 0x22, 0x11, 0x7f,
+ // Multi byte SLEB128
+ 0x11, 0xa2, 0x22, 0x11, 0xa2, 0x74, 0x11, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88,
+ 0x09, 0x11,
+ };
+ if (sizeof(TypeParam) == 4) {
+ opcode_buffer.push_back(0xb8);
+ opcode_buffer.push_back(0xd3);
+ opcode_buffer.push_back(0x63);
+ } else {
+ opcode_buffer.push_back(0x81);
+ opcode_buffer.push_back(0x82);
+ opcode_buffer.push_back(0x83);
+ opcode_buffer.push_back(0x84);
+ opcode_buffer.push_back(0x85);
+ opcode_buffer.push_back(0x86);
+ opcode_buffer.push_back(0x87);
+ opcode_buffer.push_back(0x88);
+ opcode_buffer.push_back(0x79);
+ }
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ // Single byte SLEB128
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x11, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(0x22U, this->op_->StackAt(0));
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x11, this->op_->cur_op());
+ ASSERT_EQ(2U, this->op_->StackSize());
+ ASSERT_EQ(static_cast<TypeParam>(-1), this->op_->StackAt(0));
+
+ // Multi byte SLEB128
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x11, this->op_->cur_op());
+ ASSERT_EQ(3U, this->op_->StackSize());
+ ASSERT_EQ(0x1122U, this->op_->StackAt(0));
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x11, this->op_->cur_op());
+ ASSERT_EQ(4U, this->op_->StackSize());
+ ASSERT_EQ(static_cast<TypeParam>(-1502), this->op_->StackAt(0));
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x11, this->op_->cur_op());
+ ASSERT_EQ(5U, this->op_->StackSize());
+ if (sizeof(TypeParam) == 4) {
+ ASSERT_EQ(0x5080c101U, this->op_->StackAt(0));
+ } else {
+ ASSERT_EQ(0x9101c305080c101ULL, this->op_->StackAt(0));
+ }
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x11, this->op_->cur_op());
+ ASSERT_EQ(6U, this->op_->StackSize());
+ if (sizeof(TypeParam) == 4) {
+ ASSERT_EQ(static_cast<TypeParam>(-464456), this->op_->StackAt(0));
+ } else {
+ ASSERT_EQ(static_cast<TypeParam>(-499868564803501823LL), this->op_->StackAt(0));
+ }
+}
+
+TYPED_TEST_P(DwarfOpTest, op_dup) {
+ std::vector<uint8_t> opcode_buffer = {
+ // Should fail since nothing is on the stack.
+ 0x12,
+ // Push on a value and dup.
+ 0x08, 0x15, 0x12,
+ // Do it again.
+ 0x08, 0x23, 0x12,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x12, this->op_->cur_op());
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x12, this->op_->cur_op());
+ ASSERT_EQ(2U, this->op_->StackSize());
+ ASSERT_EQ(0x15U, this->op_->StackAt(0));
+ ASSERT_EQ(0x15U, this->op_->StackAt(1));
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(3U, this->op_->StackSize());
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x12, this->op_->cur_op());
+ ASSERT_EQ(4U, this->op_->StackSize());
+ ASSERT_EQ(0x23U, this->op_->StackAt(0));
+ ASSERT_EQ(0x23U, this->op_->StackAt(1));
+ ASSERT_EQ(0x15U, this->op_->StackAt(2));
+ ASSERT_EQ(0x15U, this->op_->StackAt(3));
+}
+
+TYPED_TEST_P(DwarfOpTest, op_drop) {
+ std::vector<uint8_t> opcode_buffer = {
+ // Push a couple of values.
+ 0x08, 0x10, 0x08, 0x20,
+ // Drop the values.
+ 0x13, 0x13,
+ // Attempt to drop empty stack.
+ 0x13,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(2U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x13, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(0x10U, this->op_->StackAt(0));
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x13, this->op_->cur_op());
+ ASSERT_EQ(0U, this->op_->StackSize());
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x13, this->op_->cur_op());
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+}
+
+TYPED_TEST_P(DwarfOpTest, op_over) {
+ std::vector<uint8_t> opcode_buffer = {
+ // Push a couple of values.
+ 0x08, 0x1a, 0x08, 0xed,
+ // Copy a value.
+ 0x14,
+ // Remove all but one element.
+ 0x13, 0x13,
+ // Provoke a failure with this opcode.
+ 0x14,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(2U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x14, this->op_->cur_op());
+ ASSERT_EQ(3U, this->op_->StackSize());
+ ASSERT_EQ(0x1aU, this->op_->StackAt(0));
+ ASSERT_EQ(0xedU, this->op_->StackAt(1));
+ ASSERT_EQ(0x1aU, this->op_->StackAt(2));
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(2U, this->op_->StackSize());
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(1U, this->op_->StackSize());
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x14, this->op_->cur_op());
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+}
+
+TYPED_TEST_P(DwarfOpTest, op_pick) {
+ std::vector<uint8_t> opcode_buffer = {
+ // Push a few values.
+ 0x08, 0x1a, 0x08, 0xed, 0x08, 0x34,
+ // Copy the value at offset 2.
+ 0x15, 0x01,
+ // Copy the last value in the stack.
+ 0x15, 0x03,
+ // Choose an invalid index.
+ 0x15, 0x10,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(2U, this->op_->StackSize());
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(3U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x15, this->op_->cur_op());
+ ASSERT_EQ(4U, this->op_->StackSize());
+ ASSERT_EQ(0xedU, this->op_->StackAt(0));
+ ASSERT_EQ(0x34U, this->op_->StackAt(1));
+ ASSERT_EQ(0xedU, this->op_->StackAt(2));
+ ASSERT_EQ(0x1aU, this->op_->StackAt(3));
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x15, this->op_->cur_op());
+ ASSERT_EQ(5U, this->op_->StackSize());
+ ASSERT_EQ(0x1aU, this->op_->StackAt(0));
+ ASSERT_EQ(0xedU, this->op_->StackAt(1));
+ ASSERT_EQ(0x34U, this->op_->StackAt(2));
+ ASSERT_EQ(0xedU, this->op_->StackAt(3));
+ ASSERT_EQ(0x1aU, this->op_->StackAt(4));
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x15, this->op_->cur_op());
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+}
+
+TYPED_TEST_P(DwarfOpTest, op_swap) {
+ std::vector<uint8_t> opcode_buffer = {
+ // Push a couple of values.
+ 0x08, 0x26, 0x08, 0xab,
+ // Swap values.
+ 0x16,
+ // Pop a value to cause a failure.
+ 0x13, 0x16,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(2U, this->op_->StackSize());
+ ASSERT_EQ(0xabU, this->op_->StackAt(0));
+ ASSERT_EQ(0x26U, this->op_->StackAt(1));
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x16, this->op_->cur_op());
+ ASSERT_EQ(2U, this->op_->StackSize());
+ ASSERT_EQ(0x26U, this->op_->StackAt(0));
+ ASSERT_EQ(0xabU, this->op_->StackAt(1));
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(1U, this->op_->StackSize());
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x16, this->op_->cur_op());
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+}
+
+TYPED_TEST_P(DwarfOpTest, op_rot) {
+ std::vector<uint8_t> opcode_buffer = {
+ // Rotate that should cause a failure.
+ 0x17, 0x08, 0x10,
+ // Only 1 value on stack, should fail.
+ 0x17, 0x08, 0x20,
+ // Only 2 values on stack, should fail.
+ 0x17, 0x08, 0x30,
+ // Should rotate properly.
+ 0x17,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(1U, this->op_->StackSize());
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(2U, this->op_->StackSize());
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(3U, this->op_->StackSize());
+ ASSERT_EQ(0x30U, this->op_->StackAt(0));
+ ASSERT_EQ(0x20U, this->op_->StackAt(1));
+ ASSERT_EQ(0x10U, this->op_->StackAt(2));
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x17, this->op_->cur_op());
+ ASSERT_EQ(3U, this->op_->StackSize());
+ ASSERT_EQ(0x20U, this->op_->StackAt(0));
+ ASSERT_EQ(0x10U, this->op_->StackAt(1));
+ ASSERT_EQ(0x30U, this->op_->StackAt(2));
+}
+
+TYPED_TEST_P(DwarfOpTest, op_abs) {
+ std::vector<uint8_t> opcode_buffer = {
+ // Abs that should fail.
+ 0x19,
+ // A value that is already positive.
+ 0x08, 0x10, 0x19,
+ // A value that is negative.
+ 0x11, 0x7f, 0x19,
+ // A value that is large and negative.
+ 0x11, 0x81, 0x80, 0x80, 0x80,
+ };
+ if (sizeof(TypeParam) == 4) {
+ opcode_buffer.push_back(0x08);
+ } else {
+ opcode_buffer.push_back(0x80);
+ opcode_buffer.push_back(0x80);
+ opcode_buffer.push_back(0x01);
+ }
+ opcode_buffer.push_back(0x19);
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(0x10U, this->op_->StackAt(0));
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x19, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(0x10U, this->op_->StackAt(0));
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(2U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x19, this->op_->cur_op());
+ ASSERT_EQ(2U, this->op_->StackSize());
+ ASSERT_EQ(0x1U, this->op_->StackAt(0));
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(3U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x19, this->op_->cur_op());
+ ASSERT_EQ(3U, this->op_->StackSize());
+ if (sizeof(TypeParam) == 4) {
+ ASSERT_EQ(2147483647U, this->op_->StackAt(0));
+ } else {
+ ASSERT_EQ(4398046511105UL, this->op_->StackAt(0));
+ }
+}
+
+TYPED_TEST_P(DwarfOpTest, op_and) {
+ std::vector<uint8_t> opcode_buffer = {
+ // No stack, and op will fail.
+ 0x1b,
+ // Push a single value.
+ 0x08, 0x20,
+ // One element stack, and op will fail.
+ 0x1b,
+ // Push another value.
+ 0x08, 0x02, 0x1b,
+ // Push on two negative values.
+ 0x11, 0x7c, 0x11, 0x7f, 0x1b,
+ // Push one negative, one positive.
+ 0x11, 0x10, 0x11, 0x7c, 0x1b,
+ // Divide by zero.
+ 0x11, 0x10, 0x11, 0x00, 0x1b,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(1U, this->op_->StackSize());
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ // Two positive values.
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(2U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x1b, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(0x10U, this->op_->StackAt(0));
+
+ // Two negative values.
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(2U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(3U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x1b, this->op_->cur_op());
+ ASSERT_EQ(2U, this->op_->StackSize());
+ ASSERT_EQ(0x04U, this->op_->StackAt(0));
+
+ // One negative value, one positive value.
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(3U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(4U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x1b, this->op_->cur_op());
+ ASSERT_EQ(3U, this->op_->StackSize());
+ ASSERT_EQ(static_cast<TypeParam>(-4), this->op_->StackAt(0));
+
+ // Divide by zero.
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(4U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(5U, this->op_->StackSize());
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->last_error());
+}
+
+TYPED_TEST_P(DwarfOpTest, op_div) {
+ std::vector<uint8_t> opcode_buffer = {
+ // No stack, and op will fail.
+ 0x1a,
+ // Push a single value.
+ 0x08, 0x48,
+ // One element stack, and op will fail.
+ 0x1a,
+ // Push another value.
+ 0x08, 0xf0, 0x1a,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(1U, this->op_->StackSize());
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(2U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x1a, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(0x40U, this->op_->StackAt(0));
+}
+
+TYPED_TEST_P(DwarfOpTest, op_minus) {
+ std::vector<uint8_t> opcode_buffer = {
+ // No stack, and op will fail.
+ 0x1c,
+ // Push a single value.
+ 0x08, 0x48,
+ // One element stack, and op will fail.
+ 0x1c,
+ // Push another value.
+ 0x08, 0x04, 0x1c,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(1U, this->op_->StackSize());
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(2U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x1c, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(0x44U, this->op_->StackAt(0));
+}
+
+TYPED_TEST_P(DwarfOpTest, op_mod) {
+ std::vector<uint8_t> opcode_buffer = {
+ // No stack, and op will fail.
+ 0x1d,
+ // Push a single value.
+ 0x08, 0x47,
+ // One element stack, and op will fail.
+ 0x1d,
+ // Push another value.
+ 0x08, 0x04, 0x1d,
+ // Try a mod of zero.
+ 0x08, 0x01, 0x08, 0x00, 0x1d,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(1U, this->op_->StackSize());
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(2U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x1d, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(0x03U, this->op_->StackAt(0));
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(2U, this->op_->StackSize());
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(3U, this->op_->StackSize());
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->last_error());
+}
+
+TYPED_TEST_P(DwarfOpTest, op_mul) {
+ std::vector<uint8_t> opcode_buffer = {
+ // No stack, and op will fail.
+ 0x1e,
+ // Push a single value.
+ 0x08, 0x48,
+ // One element stack, and op will fail.
+ 0x1e,
+ // Push another value.
+ 0x08, 0x04, 0x1e,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(1U, this->op_->StackSize());
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(2U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x1e, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(0x120U, this->op_->StackAt(0));
+}
+
+TYPED_TEST_P(DwarfOpTest, op_neg) {
+ std::vector<uint8_t> opcode_buffer = {
+ // No stack, and op will fail.
+ 0x1f,
+ // Push a single value.
+ 0x08, 0x48, 0x1f,
+ // Push a negative value.
+ 0x11, 0x7f, 0x1f,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(1U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x1f, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(static_cast<TypeParam>(-72), this->op_->StackAt(0));
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(2U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x1f, this->op_->cur_op());
+ ASSERT_EQ(2U, this->op_->StackSize());
+ ASSERT_EQ(0x01U, this->op_->StackAt(0));
+}
+
+TYPED_TEST_P(DwarfOpTest, op_not) {
+ std::vector<uint8_t> opcode_buffer = {
+ // No stack, and op will fail.
+ 0x20,
+ // Push a single value.
+ 0x08, 0x4, 0x20,
+ // Push a negative value.
+ 0x11, 0x7c, 0x20,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(1U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x20, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(static_cast<TypeParam>(-5), this->op_->StackAt(0));
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(2U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x20, this->op_->cur_op());
+ ASSERT_EQ(2U, this->op_->StackSize());
+ ASSERT_EQ(0x03U, this->op_->StackAt(0));
+}
+
+TYPED_TEST_P(DwarfOpTest, op_or) {
+ std::vector<uint8_t> opcode_buffer = {
+ // No stack, and op will fail.
+ 0x21,
+ // Push a single value.
+ 0x08, 0x48,
+ // One element stack, and op will fail.
+ 0x21,
+ // Push another value.
+ 0x08, 0xf4, 0x21,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(1U, this->op_->StackSize());
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(2U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x21, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(0xfcU, this->op_->StackAt(0));
+}
+
+TYPED_TEST_P(DwarfOpTest, op_plus) {
+ std::vector<uint8_t> opcode_buffer = {
+ // No stack, and op will fail.
+ 0x22,
+ // Push a single value.
+ 0x08, 0xff,
+ // One element stack, and op will fail.
+ 0x22,
+ // Push another value.
+ 0x08, 0xf2, 0x22,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(1U, this->op_->StackSize());
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(2U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x22, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(0x1f1U, this->op_->StackAt(0));
+}
+
+TYPED_TEST_P(DwarfOpTest, op_plus_uconst) {
+ std::vector<uint8_t> opcode_buffer = {
+ // No stack, and op will fail.
+ 0x23,
+ // Push a single value.
+ 0x08, 0x50, 0x23, 0x80, 0x51,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(1U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x23, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(0x28d0U, this->op_->StackAt(0));
+}
+
+TYPED_TEST_P(DwarfOpTest, op_shl) {
+ std::vector<uint8_t> opcode_buffer = {
+ // No stack, and op will fail.
+ 0x24,
+ // Push a single value.
+ 0x08, 0x67,
+ // One element stack, and op will fail.
+ 0x24,
+ // Push another value.
+ 0x08, 0x03, 0x24,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(1U, this->op_->StackSize());
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(2U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x24, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(0x338U, this->op_->StackAt(0));
+}
+
+TYPED_TEST_P(DwarfOpTest, op_shr) {
+ std::vector<uint8_t> opcode_buffer = {
+ // No stack, and op will fail.
+ 0x25,
+ // Push a single value.
+ 0x11, 0x70,
+ // One element stack, and op will fail.
+ 0x25,
+ // Push another value.
+ 0x08, 0x03, 0x25,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(1U, this->op_->StackSize());
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(2U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x25, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ if (sizeof(TypeParam) == 4) {
+ ASSERT_EQ(0x1ffffffeU, this->op_->StackAt(0));
+ } else {
+ ASSERT_EQ(0x1ffffffffffffffeULL, this->op_->StackAt(0));
+ }
+}
+
+TYPED_TEST_P(DwarfOpTest, op_shra) {
+ std::vector<uint8_t> opcode_buffer = {
+ // No stack, and op will fail.
+ 0x26,
+ // Push a single value.
+ 0x11, 0x70,
+ // One element stack, and op will fail.
+ 0x26,
+ // Push another value.
+ 0x08, 0x03, 0x26,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(1U, this->op_->StackSize());
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(2U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x26, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(static_cast<TypeParam>(-2), this->op_->StackAt(0));
+}
+
+TYPED_TEST_P(DwarfOpTest, op_xor) {
+ std::vector<uint8_t> opcode_buffer = {
+ // No stack, and op will fail.
+ 0x27,
+ // Push a single value.
+ 0x08, 0x11,
+ // One element stack, and op will fail.
+ 0x27,
+ // Push another value.
+ 0x08, 0x41, 0x27,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(1U, this->op_->StackSize());
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(2U, this->op_->StackSize());
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x27, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(0x50U, this->op_->StackAt(0));
+}
+
+TYPED_TEST_P(DwarfOpTest, op_bra) {
+ std::vector<uint8_t> opcode_buffer = {
+ // No stack, and op will fail.
+ 0x28,
+ // Push on a non-zero value with a positive branch.
+ 0x08, 0x11, 0x28, 0x02, 0x01,
+ // Push on a zero value with a positive branch.
+ 0x08, 0x00, 0x28, 0x05, 0x00,
+ // Push on a non-zero value with a negative branch.
+ 0x08, 0x11, 0x28, 0xfc, 0xff,
+ // Push on a zero value with a negative branch.
+ 0x08, 0x00, 0x28, 0xf0, 0xff,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_FALSE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ // Push on a non-zero value with a positive branch.
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(1U, this->op_->StackSize());
+
+ uint64_t offset = this->mem_->cur_offset() + 3;
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x28, this->op_->cur_op());
+ ASSERT_EQ(0U, this->op_->StackSize());
+ ASSERT_EQ(offset + 0x102, this->mem_->cur_offset());
+
+ // Push on a zero value with a positive branch.
+ this->mem_->set_cur_offset(offset);
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(1U, this->op_->StackSize());
+
+ offset = this->mem_->cur_offset() + 3;
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x28, this->op_->cur_op());
+ ASSERT_EQ(0U, this->op_->StackSize());
+ ASSERT_EQ(offset - 5, this->mem_->cur_offset());
+
+ // Push on a non-zero value with a negative branch.
+ this->mem_->set_cur_offset(offset);
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(1U, this->op_->StackSize());
+
+ offset = this->mem_->cur_offset() + 3;
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x28, this->op_->cur_op());
+ ASSERT_EQ(0U, this->op_->StackSize());
+ ASSERT_EQ(offset - 4, this->mem_->cur_offset());
+
+ // Push on a zero value with a negative branch.
+ this->mem_->set_cur_offset(offset);
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(1U, this->op_->StackSize());
+
+ offset = this->mem_->cur_offset() + 3;
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x28, this->op_->cur_op());
+ ASSERT_EQ(0U, this->op_->StackSize());
+ ASSERT_EQ(offset + 16, this->mem_->cur_offset());
+}
+
+TYPED_TEST_P(DwarfOpTest, compare_opcode_stack_error) {
+ // All of the ops require two stack elements. Loop through all of these
+ // ops with potential errors.
+ std::vector<uint8_t> opcode_buffer = {
+ 0xff, // Place holder for compare op.
+ 0x08, 0x11,
+ 0xff, // Place holder for compare op.
+ };
+
+ for (uint8_t opcode = 0x29; opcode <= 0x2e; opcode++) {
+ opcode_buffer[0] = opcode;
+ opcode_buffer[3] = opcode;
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_FALSE(this->op_->Eval(0, 1, DWARF_VERSION_MAX));
+ ASSERT_EQ(opcode, this->op_->cur_op());
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+
+ ASSERT_FALSE(this->op_->Eval(1, 4, DWARF_VERSION_MAX));
+ ASSERT_EQ(opcode, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(DWARF_ERROR_STACK_INDEX_NOT_VALID, this->op_->last_error());
+ }
+}
+
+TYPED_TEST_P(DwarfOpTest, compare_opcodes) {
+ // Have three different checks for each compare op:
+ // - Both values the same.
+ // - The first value larger than the second.
+ // - The second value larger than the first.
+ std::vector<uint8_t> opcode_buffer = {
+ // Values the same.
+ 0x08, 0x11, 0x08, 0x11,
+ 0xff, // Placeholder.
+ // First value larger.
+ 0x08, 0x12, 0x08, 0x10,
+ 0xff, // Placeholder.
+ // Second value larger.
+ 0x08, 0x10, 0x08, 0x12,
+ 0xff, // Placeholder.
+ };
+
+ // Opcode followed by the expected values on the stack.
+ std::vector<uint8_t> expected = {
+ 0x29, 1, 0, 0, // eq
+ 0x2a, 1, 1, 0, // ge
+ 0x2b, 0, 1, 0, // gt
+ 0x2c, 1, 0, 1, // le
+ 0x2d, 0, 0, 1, // lt
+ 0x2e, 0, 1, 1, // ne
+ };
+ for (size_t i = 0; i < expected.size(); i += 4) {
+ opcode_buffer[4] = expected[i];
+ opcode_buffer[9] = expected[i];
+ opcode_buffer[14] = expected[i];
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_TRUE(this->op_->Eval(0, 15, DWARF_VERSION_MAX))
+ << "Op: 0x" << std::hex << static_cast<uint32_t>(expected[i]) << " failed";
+
+ ASSERT_EQ(3U, this->op_->StackSize());
+ ASSERT_EQ(expected[i + 1], this->op_->StackAt(2));
+ ASSERT_EQ(expected[i + 2], this->op_->StackAt(1));
+ ASSERT_EQ(expected[i + 3], this->op_->StackAt(0));
+ }
+}
+
+TYPED_TEST_P(DwarfOpTest, op_skip) {
+ std::vector<uint8_t> opcode_buffer = {
+ // Positive value.
+ 0x2f, 0x10, 0x20,
+ // Negative value.
+ 0x2f, 0xfd, 0xff,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ uint64_t offset = this->mem_->cur_offset() + 3;
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x2f, this->op_->cur_op());
+ ASSERT_EQ(0U, this->op_->StackSize());
+ ASSERT_EQ(offset + 0x2010, this->mem_->cur_offset());
+
+ this->mem_->set_cur_offset(offset);
+ offset = this->mem_->cur_offset() + 3;
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x2f, this->op_->cur_op());
+ ASSERT_EQ(0U, this->op_->StackSize());
+ ASSERT_EQ(offset - 3, this->mem_->cur_offset());
+}
+
+TYPED_TEST_P(DwarfOpTest, op_lit) {
+ std::vector<uint8_t> opcode_buffer;
+
+ // Verify every lit opcode.
+ for (uint8_t op = 0x30; op <= 0x4f; op++) {
+ opcode_buffer.push_back(op);
+ }
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ for (size_t i = 0; i < opcode_buffer.size(); i++) {
+ uint32_t op = opcode_buffer[i];
+ ASSERT_TRUE(this->op_->Eval(i, i + 1, DWARF_VERSION_MAX)) << "Failed op: 0x" << std::hex << op;
+ ASSERT_EQ(op, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize()) << "Failed op: 0x" << std::hex << op;
+ ASSERT_EQ(op - 0x30U, this->op_->StackAt(0)) << "Failed op: 0x" << std::hex << op;
+ }
+}
+
+TYPED_TEST_P(DwarfOpTest, op_reg) {
+ std::vector<uint8_t> opcode_buffer;
+
+ // Verify every reg opcode.
+ for (uint8_t op = 0x50; op <= 0x6f; op++) {
+ opcode_buffer.push_back(op);
+ }
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ for (size_t i = 0; i < opcode_buffer.size(); i++) {
+ uint32_t op = opcode_buffer[i];
+ ASSERT_TRUE(this->op_->Eval(i, i + 1, DWARF_VERSION_MAX)) << "Failed op: 0x" << std::hex << op;
+ ASSERT_EQ(op, this->op_->cur_op());
+ ASSERT_TRUE(this->op_->is_register()) << "Failed op: 0x" << std::hex << op;
+ ASSERT_EQ(1U, this->op_->StackSize()) << "Failed op: 0x" << std::hex << op;
+ ASSERT_EQ(op - 0x50U, this->op_->StackAt(0)) << "Failed op: 0x" << std::hex << op;
+ }
+}
+
+TYPED_TEST_P(DwarfOpTest, op_regx) {
+ std::vector<uint8_t> opcode_buffer = {
+ 0x90, 0x02, 0x90, 0x80, 0x15,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ ASSERT_TRUE(this->op_->Eval(0, 2, DWARF_VERSION_MAX));
+ ASSERT_EQ(0x90, this->op_->cur_op());
+ ASSERT_TRUE(this->op_->is_register());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(0x02U, this->op_->StackAt(0));
+
+ ASSERT_TRUE(this->op_->Eval(2, 5, DWARF_VERSION_MAX));
+ ASSERT_EQ(0x90, this->op_->cur_op());
+ ASSERT_TRUE(this->op_->is_register());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(0xa80U, this->op_->StackAt(0));
+}
+
+TYPED_TEST_P(DwarfOpTest, op_breg) {
+ std::vector<uint8_t> opcode_buffer;
+
+ // Verify every reg opcode.
+ for (uint8_t op = 0x70; op <= 0x8f; op++) {
+ // Positive value added to register.
+ opcode_buffer.push_back(op);
+ opcode_buffer.push_back(0x12);
+ // Negative value added to register.
+ opcode_buffer.push_back(op);
+ opcode_buffer.push_back(0x7e);
+ }
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ RegsFake<TypeParam> regs(32, 10);
+ for (size_t i = 0; i < 32; i++) {
+ regs[i] = i + 10;
+ }
+ this->op_->set_regs(®s);
+
+ uint64_t offset = 0;
+ for (uint32_t op = 0x70; op <= 0x8f; op++) {
+ // Positive value added to register.
+ ASSERT_TRUE(this->op_->Eval(offset, offset + 2, DWARF_VERSION_MAX)) << "Failed op: 0x"
+ << std::hex << op;
+ ASSERT_EQ(op, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize()) << "Failed op: 0x" << std::hex << op;
+ ASSERT_EQ(op - 0x70 + 10 + 0x12, this->op_->StackAt(0)) << "Failed op: 0x" << std::hex << op;
+ offset += 2;
+
+ // Negative value added to register.
+ ASSERT_TRUE(this->op_->Eval(offset, offset + 2, DWARF_VERSION_MAX)) << "Failed op: 0x"
+ << std::hex << op;
+ ASSERT_EQ(op, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize()) << "Failed op: 0x" << std::hex << op;
+ ASSERT_EQ(op - 0x70 + 10 - 2, this->op_->StackAt(0)) << "Failed op: 0x" << std::hex << op;
+ offset += 2;
+ }
+}
+
+TYPED_TEST_P(DwarfOpTest, op_breg_invalid_register) {
+ std::vector<uint8_t> opcode_buffer = {
+ 0x7f, 0x12, 0x80, 0x12,
+ };
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ RegsFake<TypeParam> regs(16, 10);
+ for (size_t i = 0; i < 16; i++) {
+ regs[i] = i + 10;
+ }
+ this->op_->set_regs(®s);
+
+ // Should pass since this references the last regsister.
+ ASSERT_TRUE(this->op_->Eval(0, 2, DWARF_VERSION_MAX));
+ ASSERT_EQ(0x7fU, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(0x2bU, this->op_->StackAt(0));
+
+ // Should fail since this references a non-existent register.
+ ASSERT_FALSE(this->op_->Eval(2, 4, DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->last_error());
+}
+
+TYPED_TEST_P(DwarfOpTest, op_bregx) {
+ std::vector<uint8_t> opcode_buffer = {// Positive value added to register.
+ 0x92, 0x05, 0x20,
+ // Negative value added to register.
+ 0x92, 0x06, 0x80, 0x7e,
+ // Illegal register.
+ 0x92, 0x80, 0x15, 0x80, 0x02};
+ this->op_memory_.SetMemory(0, opcode_buffer);
+
+ RegsFake<TypeParam> regs(10, 10);
+ regs[5] = 0x45;
+ regs[6] = 0x190;
+ this->op_->set_regs(®s);
+
+ ASSERT_TRUE(this->op_->Eval(0, 3, DWARF_VERSION_MAX));
+ ASSERT_EQ(0x92, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(0x65U, this->op_->StackAt(0));
+
+ ASSERT_TRUE(this->op_->Eval(3, 7, DWARF_VERSION_MAX));
+ ASSERT_EQ(0x92, this->op_->cur_op());
+ ASSERT_EQ(1U, this->op_->StackSize());
+ ASSERT_EQ(0x90U, this->op_->StackAt(0));
+
+ ASSERT_FALSE(this->op_->Eval(7, 12, DWARF_VERSION_MAX));
+ ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->op_->last_error());
+}
+
+TYPED_TEST_P(DwarfOpTest, op_nop) {
+ this->op_memory_.SetMemory(0, std::vector<uint8_t>{0x96});
+
+ ASSERT_TRUE(this->op_->Decode(DWARF_VERSION_MAX));
+ ASSERT_EQ(0x96, this->op_->cur_op());
+ ASSERT_EQ(0U, this->op_->StackSize());
+}
+
+REGISTER_TYPED_TEST_CASE_P(DwarfOpTest, decode, eval, illegal_opcode, illegal_in_version3,
+ illegal_in_version4, not_implemented, op_addr, op_deref, op_deref_size,
+ const_unsigned, const_signed, const_uleb, const_sleb, op_dup, op_drop,
+ op_over, op_pick, op_swap, op_rot, op_abs, op_and, op_div, op_minus,
+ op_mod, op_mul, op_neg, op_not, op_or, op_plus, op_plus_uconst, op_shl,
+ op_shr, op_shra, op_xor, op_bra, compare_opcode_stack_error,
+ compare_opcodes, op_skip, op_lit, op_reg, op_regx, op_breg,
+ op_breg_invalid_register, op_bregx, op_nop);
+
+typedef ::testing::Types<uint32_t, uint64_t> DwarfOpTestTypes;
+INSTANTIATE_TYPED_TEST_CASE_P(, DwarfOpTest, DwarfOpTestTypes);
diff --git a/libunwindstack/tests/MemoryBuffer.cpp b/libunwindstack/tests/MemoryBuffer.cpp
new file mode 100644
index 0000000..af3d6b9
--- /dev/null
+++ b/libunwindstack/tests/MemoryBuffer.cpp
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "Memory.h"
+
+#include "LogFake.h"
+
+class MemoryBufferTest : public ::testing::Test {
+ protected:
+ void SetUp() override {
+ ResetLogs();
+ memory_.reset(new MemoryBuffer);
+ }
+ std::unique_ptr<MemoryBuffer> memory_;
+};
+
+TEST_F(MemoryBufferTest, empty) {
+ ASSERT_EQ(0U, memory_->Size());
+ std::vector<uint8_t> buffer(1024);
+ ASSERT_FALSE(memory_->Read(0, buffer.data(), 1));
+ ASSERT_EQ(nullptr, memory_->GetPtr(0));
+ ASSERT_EQ(nullptr, memory_->GetPtr(1));
+}
+
+TEST_F(MemoryBufferTest, write_read) {
+ memory_->Resize(256);
+ ASSERT_EQ(256U, memory_->Size());
+ ASSERT_TRUE(memory_->GetPtr(0) != nullptr);
+ ASSERT_TRUE(memory_->GetPtr(1) != nullptr);
+ ASSERT_TRUE(memory_->GetPtr(255) != nullptr);
+ ASSERT_TRUE(memory_->GetPtr(256) == nullptr);
+
+ uint8_t* data = memory_->GetPtr(0);
+ for (size_t i = 0; i < memory_->Size(); i++) {
+ data[i] = i;
+ }
+
+ std::vector<uint8_t> buffer(memory_->Size());
+ ASSERT_TRUE(memory_->Read(0, buffer.data(), buffer.size()));
+ for (size_t i = 0; i < buffer.size(); i++) {
+ ASSERT_EQ(i, buffer[i]) << "Failed at byte " << i;
+ }
+}
+
+TEST_F(MemoryBufferTest, read_failures) {
+ memory_->Resize(100);
+ std::vector<uint8_t> buffer(200);
+ ASSERT_FALSE(memory_->Read(0, buffer.data(), 101));
+ ASSERT_FALSE(memory_->Read(100, buffer.data(), 1));
+ ASSERT_FALSE(memory_->Read(101, buffer.data(), 2));
+ ASSERT_FALSE(memory_->Read(99, buffer.data(), 2));
+ ASSERT_TRUE(memory_->Read(99, buffer.data(), 1));
+}
+
+TEST_F(MemoryBufferTest, read_failure_overflow) {
+ memory_->Resize(100);
+ std::vector<uint8_t> buffer(200);
+
+ ASSERT_FALSE(memory_->Read(UINT64_MAX - 100, buffer.data(), 200));
+}
diff --git a/libunwindstack/tests/MemoryFake.h b/libunwindstack/tests/MemoryFake.h
index e05736b..70ef30a 100644
--- a/libunwindstack/tests/MemoryFake.h
+++ b/libunwindstack/tests/MemoryFake.h
@@ -75,4 +75,16 @@
}
};
+class MemoryFakeRemote : public MemoryRemote {
+ public:
+ MemoryFakeRemote() : MemoryRemote(0) {}
+ virtual ~MemoryFakeRemote() = default;
+
+ protected:
+ bool PtraceRead(uint64_t, long* value) override {
+ *value = 0;
+ return true;
+ }
+};
+
#endif // _LIBUNWINDSTACK_TESTS_MEMORY_FAKE_H
diff --git a/libunwindstack/tests/MemoryFileTest.cpp b/libunwindstack/tests/MemoryFileTest.cpp
index 870ca19..aa7a23a 100644
--- a/libunwindstack/tests/MemoryFileTest.cpp
+++ b/libunwindstack/tests/MemoryFileTest.cpp
@@ -14,6 +14,9 @@
* limitations under the License.
*/
+#include <string>
+#include <vector>
+
#include <android-base/test_utils.h>
#include <android-base/file.h>
#include <gtest/gtest.h>
@@ -39,7 +42,7 @@
TemporaryFile* tf_ = nullptr;
};
-TEST_F(MemoryFileTest, offset_0) {
+TEST_F(MemoryFileTest, init_offset_0) {
WriteTestData();
ASSERT_TRUE(memory_.Init(tf_->path, 0));
@@ -49,7 +52,7 @@
ASSERT_STREQ("0123456789", buffer.data());
}
-TEST_F(MemoryFileTest, offset_non_zero) {
+TEST_F(MemoryFileTest, init_offset_non_zero) {
WriteTestData();
ASSERT_TRUE(memory_.Init(tf_->path, 10));
@@ -59,7 +62,7 @@
ASSERT_STREQ("abcdefghij", buffer.data());
}
-TEST_F(MemoryFileTest, offset_non_zero_larger_than_pagesize) {
+TEST_F(MemoryFileTest, init_offset_non_zero_larger_than_pagesize) {
size_t pagesize = getpagesize();
std::string large_string;
for (size_t i = 0; i < pagesize; i++) {
@@ -75,7 +78,7 @@
ASSERT_STREQ("abcdefgh", buffer.data());
}
-TEST_F(MemoryFileTest, offset_pagesize_aligned) {
+TEST_F(MemoryFileTest, init_offset_pagesize_aligned) {
size_t pagesize = getpagesize();
std::string data;
for (size_t i = 0; i < 2 * pagesize; i++) {
@@ -96,7 +99,7 @@
ASSERT_STREQ(expected_str.c_str(), buffer.data());
}
-TEST_F(MemoryFileTest, offset_pagesize_aligned_plus_extra) {
+TEST_F(MemoryFileTest, init_offset_pagesize_aligned_plus_extra) {
size_t pagesize = getpagesize();
std::string data;
for (size_t i = 0; i < 2 * pagesize; i++) {
@@ -117,6 +120,23 @@
ASSERT_STREQ(expected_str.c_str(), buffer.data());
}
+TEST_F(MemoryFileTest, init_offset_greater_than_filesize) {
+ size_t pagesize = getpagesize();
+ std::string data;
+ uint64_t file_size = 2 * pagesize + pagesize / 2;
+ for (size_t i = 0; i < file_size; i++) {
+ data += static_cast<char>((i / pagesize) + '0');
+ }
+ ASSERT_TRUE(android::base::WriteStringToFd(data, tf_->fd));
+
+ // Check offset > file size fails and aligned_offset > file size.
+ ASSERT_FALSE(memory_.Init(tf_->path, file_size + 2 * pagesize));
+ // Check offset == filesize fails.
+ ASSERT_FALSE(memory_.Init(tf_->path, file_size));
+ // Check aligned_offset < filesize, but offset > filesize fails.
+ ASSERT_FALSE(memory_.Init(tf_->path, 2 * pagesize + pagesize / 2 + pagesize / 4));
+}
+
TEST_F(MemoryFileTest, read_error) {
std::string data;
for (size_t i = 0; i < 5000; i++) {
@@ -137,32 +157,9 @@
ASSERT_TRUE(memory_.Read(4990, buffer.data(), 10));
ASSERT_FALSE(memory_.Read(4999, buffer.data(), 2));
ASSERT_TRUE(memory_.Read(4999, buffer.data(), 1));
-}
-TEST_F(MemoryFileTest, read_string) {
- std::string value("name_in_file");
- ASSERT_TRUE(android::base::WriteFully(tf_->fd, value.c_str(), value.size() + 1));
-
- std::string name;
- ASSERT_TRUE(memory_.Init(tf_->path, 0));
- ASSERT_TRUE(memory_.ReadString(0, &name));
- ASSERT_EQ("name_in_file", name);
- ASSERT_TRUE(memory_.ReadString(5, &name));
- ASSERT_EQ("in_file", name);
-}
-
-TEST_F(MemoryFileTest, read_string_error) {
- std::vector<uint8_t> buffer = { 0x23, 0x32, 0x45 };
- ASSERT_TRUE(android::base::WriteFully(tf_->fd, buffer.data(), buffer.size()));
-
- std::string name;
- ASSERT_TRUE(memory_.Init(tf_->path, 0));
-
- // Read from a non-existant address.
- ASSERT_FALSE(memory_.ReadString(100, &name));
-
- // This should fail because there is no terminating \0
- ASSERT_FALSE(memory_.ReadString(0, &name));
+ // Check that overflow fails properly.
+ ASSERT_FALSE(memory_.Read(UINT64_MAX - 100, buffer.data(), 200));
}
TEST_F(MemoryFileTest, read_past_file_within_mapping) {
diff --git a/libunwindstack/tests/MemoryLocalTest.cpp b/libunwindstack/tests/MemoryLocalTest.cpp
index 0ba5f1c..ab999da 100644
--- a/libunwindstack/tests/MemoryLocalTest.cpp
+++ b/libunwindstack/tests/MemoryLocalTest.cpp
@@ -47,25 +47,6 @@
}
}
-TEST(MemoryLocalTest, read_string) {
- std::string name("string_in_memory");
-
- MemoryLocal local;
-
- std::vector<uint8_t> dst(1024);
- std::string dst_name;
- ASSERT_TRUE(local.ReadString(reinterpret_cast<uint64_t>(name.c_str()), &dst_name));
- ASSERT_EQ("string_in_memory", dst_name);
-
- ASSERT_TRUE(local.ReadString(reinterpret_cast<uint64_t>(&name[7]), &dst_name));
- ASSERT_EQ("in_memory", dst_name);
-
- ASSERT_TRUE(local.ReadString(reinterpret_cast<uint64_t>(&name[7]), &dst_name, 10));
- ASSERT_EQ("in_memory", dst_name);
-
- ASSERT_FALSE(local.ReadString(reinterpret_cast<uint64_t>(&name[7]), &dst_name, 9));
-}
-
TEST(MemoryLocalTest, read_illegal) {
MemoryLocal local;
@@ -73,3 +54,13 @@
ASSERT_FALSE(local.Read(0, dst.data(), 1));
ASSERT_FALSE(local.Read(0, dst.data(), 100));
}
+
+TEST(MemoryLocalTest, read_overflow) {
+ MemoryLocal local;
+
+ // On 32 bit this test doesn't necessarily cause an overflow. The 64 bit
+ // version will always go through the overflow check.
+ std::vector<uint8_t> dst(100);
+ uint64_t value;
+ ASSERT_FALSE(local.Read(reinterpret_cast<uint64_t>(&value), dst.data(), SIZE_MAX));
+}
diff --git a/libunwindstack/tests/MemoryRangeTest.cpp b/libunwindstack/tests/MemoryRangeTest.cpp
index d636ec4..ee5ba01 100644
--- a/libunwindstack/tests/MemoryRangeTest.cpp
+++ b/libunwindstack/tests/MemoryRangeTest.cpp
@@ -17,6 +17,7 @@
#include <stdint.h>
#include <string.h>
+#include <memory>
#include <vector>
#include <gtest/gtest.h>
@@ -65,35 +66,14 @@
ASSERT_FALSE(range.Read(1020, dst.data(), 5));
ASSERT_FALSE(range.Read(1024, dst.data(), 1));
ASSERT_FALSE(range.Read(1024, dst.data(), 1024));
+
+ // Verify that reading up to the end works.
+ ASSERT_TRUE(range.Read(1020, dst.data(), 4));
}
-TEST_F(MemoryRangeTest, read_string_past_end) {
- std::string name("0123456789");
- memory_->SetMemory(0, name);
+TEST_F(MemoryRangeTest, read_overflow) {
+ std::vector<uint8_t> buffer(100);
- // Verify a read past the range fails.
- MemoryRange range(memory_, 0, 5);
- std::string dst_name;
- ASSERT_FALSE(range.ReadString(0, &dst_name));
-}
-
-TEST_F(MemoryRangeTest, read_string_to_end) {
- std::string name("0123456789");
- memory_->SetMemory(30, name);
-
- // Verify the range going to the end of the string works.
- MemoryRange range(memory_, 30, 30 + name.size() + 1);
- std::string dst_name;
- ASSERT_TRUE(range.ReadString(0, &dst_name));
- ASSERT_EQ("0123456789", dst_name);
-}
-
-TEST_F(MemoryRangeTest, read_string_fencepost) {
- std::string name("0123456789");
- memory_->SetMemory(10, name);
-
- // Verify the range set to one byte less than the end of the string fails.
- MemoryRange range(memory_, 10, 10 + name.size());
- std::string dst_name;
- ASSERT_FALSE(range.ReadString(0, &dst_name));
+ std::unique_ptr<MemoryRange> overflow(new MemoryRange(new MemoryFakeAlwaysReadZero, 100, 200));
+ ASSERT_FALSE(overflow->Read(UINT64_MAX - 10, buffer.data(), 100));
}
diff --git a/libunwindstack/tests/MemoryRemoteTest.cpp b/libunwindstack/tests/MemoryRemoteTest.cpp
index 7664c3e..e48edf7 100644
--- a/libunwindstack/tests/MemoryRemoteTest.cpp
+++ b/libunwindstack/tests/MemoryRemoteTest.cpp
@@ -33,6 +33,8 @@
#include "Memory.h"
+#include "MemoryFake.h"
+
class MemoryRemoteTest : public ::testing::Test {
protected:
static uint64_t NanoTime() {
@@ -121,6 +123,9 @@
ASSERT_TRUE(remote.Read(reinterpret_cast<uint64_t>(src) + pagesize - 1, dst.data(), 1));
ASSERT_FALSE(remote.Read(reinterpret_cast<uint64_t>(src) + pagesize - 4, dst.data(), 8));
+ // Check overflow condition is caught properly.
+ ASSERT_FALSE(remote.Read(UINT64_MAX - 100, dst.data(), 200));
+
ASSERT_EQ(0, munmap(src, pagesize));
ASSERT_TRUE(Detach(pid));
@@ -128,6 +133,14 @@
kill(pid, SIGKILL);
}
+TEST_F(MemoryRemoteTest, read_overflow) {
+ MemoryFakeRemote remote;
+
+ // Check overflow condition is caught properly.
+ std::vector<uint8_t> dst(200);
+ ASSERT_FALSE(remote.Read(UINT64_MAX - 100, dst.data(), 200));
+}
+
TEST_F(MemoryRemoteTest, read_illegal) {
pid_t pid;
if ((pid = fork()) == 0) {
diff --git a/libunwindstack/tests/MemoryTest.cpp b/libunwindstack/tests/MemoryTest.cpp
new file mode 100644
index 0000000..51b5d7d
--- /dev/null
+++ b/libunwindstack/tests/MemoryTest.cpp
@@ -0,0 +1,126 @@
+/*
+ * 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 <string.h>
+
+#include <string>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include "Memory.h"
+
+#include "MemoryFake.h"
+
+TEST(MemoryTest, read32) {
+ MemoryFakeAlwaysReadZero memory;
+
+ uint32_t data = 0xffffffff;
+ ASSERT_TRUE(memory.Read32(0, &data));
+ ASSERT_EQ(0U, data);
+}
+
+TEST(MemoryTest, read64) {
+ MemoryFakeAlwaysReadZero memory;
+
+ uint64_t data = 0xffffffffffffffffULL;
+ ASSERT_TRUE(memory.Read64(0, &data));
+ ASSERT_EQ(0U, data);
+}
+
+struct FakeStruct {
+ int one;
+ bool two;
+ uint32_t three;
+ uint64_t four;
+};
+
+TEST(MemoryTest, read_field) {
+ MemoryFakeAlwaysReadZero memory;
+
+ FakeStruct data;
+ memset(&data, 0xff, sizeof(data));
+ ASSERT_TRUE(memory.ReadField(0, &data, &data.one, sizeof(data.one)));
+ ASSERT_EQ(0, data.one);
+
+ memset(&data, 0xff, sizeof(data));
+ ASSERT_TRUE(memory.ReadField(0, &data, &data.two, sizeof(data.two)));
+ ASSERT_FALSE(data.two);
+
+ memset(&data, 0xff, sizeof(data));
+ ASSERT_TRUE(memory.ReadField(0, &data, &data.three, sizeof(data.three)));
+ ASSERT_EQ(0U, data.three);
+
+ memset(&data, 0xff, sizeof(data));
+ ASSERT_TRUE(memory.ReadField(0, &data, &data.four, sizeof(data.four)));
+ ASSERT_EQ(0U, data.four);
+}
+
+TEST(MemoryTest, read_field_fails) {
+ MemoryFakeAlwaysReadZero memory;
+
+ FakeStruct data;
+ memset(&data, 0xff, sizeof(data));
+
+ ASSERT_FALSE(memory.ReadField(UINT64_MAX, &data, &data.three, sizeof(data.three)));
+
+ // Field and start reversed, should fail.
+ ASSERT_FALSE(memory.ReadField(100, &data.two, &data, sizeof(data.two)));
+ ASSERT_FALSE(memory.ReadField(0, &data.two, &data, sizeof(data.two)));
+}
+
+TEST(MemoryTest, read_string) {
+ std::string name("string_in_memory");
+
+ MemoryFake memory;
+
+ memory.SetMemory(100, name.c_str(), name.size() + 1);
+
+ std::string dst_name;
+ ASSERT_TRUE(memory.ReadString(100, &dst_name));
+ ASSERT_EQ("string_in_memory", dst_name);
+
+ ASSERT_TRUE(memory.ReadString(107, &dst_name));
+ ASSERT_EQ("in_memory", dst_name);
+
+ // Set size greater than string.
+ ASSERT_TRUE(memory.ReadString(107, &dst_name, 10));
+ ASSERT_EQ("in_memory", dst_name);
+
+ ASSERT_FALSE(memory.ReadString(107, &dst_name, 9));
+}
+
+TEST(MemoryTest, read_string_error) {
+ std::string name("short");
+
+ MemoryFake memory;
+
+ // Save everything except the terminating '\0'.
+ memory.SetMemory(0, name.c_str(), name.size());
+
+ std::string dst_name;
+ // Read from a non-existant address.
+ ASSERT_FALSE(memory.ReadString(100, &dst_name));
+
+ // This should fail because there is no terminating '\0'.
+ ASSERT_FALSE(memory.ReadString(0, &dst_name));
+
+ // This should pass because there is a terminating '\0'.
+ memory.SetData8(name.size(), '\0');
+ ASSERT_TRUE(memory.ReadString(0, &dst_name));
+ ASSERT_EQ("short", dst_name);
+}
diff --git a/libunwindstack/tests/SymbolsTest.cpp b/libunwindstack/tests/SymbolsTest.cpp
new file mode 100644
index 0000000..a0a21e6
--- /dev/null
+++ b/libunwindstack/tests/SymbolsTest.cpp
@@ -0,0 +1,335 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <elf.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/ptrace.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/test_utils.h>
+#include <gtest/gtest.h>
+
+#include "Memory.h"
+#include "MemoryFake.h"
+#include "Symbols.h"
+
+template <typename TypeParam>
+class SymbolsTest : public ::testing::Test {
+ protected:
+ void SetUp() override { memory_.Clear(); }
+
+ void InitSym(TypeParam* sym, uint32_t st_value, uint32_t st_size, uint32_t st_name) {
+ memset(sym, 0, sizeof(*sym));
+ sym->st_info = STT_FUNC;
+ sym->st_value = st_value;
+ sym->st_size = st_size;
+ sym->st_name = st_name;
+ sym->st_shndx = SHN_COMMON;
+ }
+
+ MemoryFake memory_;
+};
+TYPED_TEST_CASE_P(SymbolsTest);
+
+TYPED_TEST_P(SymbolsTest, function_bounds_check) {
+ Symbols symbols(0x1000, sizeof(TypeParam), sizeof(TypeParam), 0x2000, 0x100);
+
+ TypeParam sym;
+ this->InitSym(&sym, 0x5000, 0x10, 0x40);
+ uint64_t offset = 0x1000;
+ this->memory_.SetMemory(offset, &sym, sizeof(sym));
+
+ std::string fake_name("fake_function");
+ this->memory_.SetMemory(0x2040, fake_name.c_str(), fake_name.size() + 1);
+
+ std::string name;
+ uint64_t func_offset;
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0x5000, 0, &this->memory_, &name, &func_offset));
+ ASSERT_EQ("fake_function", name);
+ ASSERT_EQ(0U, func_offset);
+
+ name.clear();
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0x500f, 0, &this->memory_, &name, &func_offset));
+ ASSERT_EQ("fake_function", name);
+ ASSERT_EQ(0xfU, func_offset);
+
+ // Check one before and one after the function.
+ ASSERT_FALSE(symbols.GetName<TypeParam>(0x4fff, 0, &this->memory_, &name, &func_offset));
+ ASSERT_FALSE(symbols.GetName<TypeParam>(0x5010, 0, &this->memory_, &name, &func_offset));
+}
+
+TYPED_TEST_P(SymbolsTest, no_symbol) {
+ Symbols symbols(0x1000, sizeof(TypeParam), sizeof(TypeParam), 0x2000, 0x100);
+
+ TypeParam sym;
+ this->InitSym(&sym, 0x5000, 0x10, 0x40);
+ uint64_t offset = 0x1000;
+ this->memory_.SetMemory(offset, &sym, sizeof(sym));
+
+ std::string fake_name("fake_function");
+ this->memory_.SetMemory(0x2040, fake_name.c_str(), fake_name.size() + 1);
+
+ // First verify that we can get the name.
+ std::string name;
+ uint64_t func_offset;
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0x5000, 0, &this->memory_, &name, &func_offset));
+ ASSERT_EQ("fake_function", name);
+ ASSERT_EQ(0U, func_offset);
+
+ // Now modify the info field so it's no longer a function.
+ sym.st_info = 0;
+ this->memory_.SetMemory(offset, &sym, sizeof(sym));
+ // Clear the cache to force the symbol data to be re-read.
+ symbols.ClearCache();
+ ASSERT_FALSE(symbols.GetName<TypeParam>(0x5000, 0, &this->memory_, &name, &func_offset));
+
+ // Set the function back, and set the shndx to UNDEF.
+ sym.st_info = STT_FUNC;
+ sym.st_shndx = SHN_UNDEF;
+ this->memory_.SetMemory(offset, &sym, sizeof(sym));
+ // Clear the cache to force the symbol data to be re-read.
+ symbols.ClearCache();
+ ASSERT_FALSE(symbols.GetName<TypeParam>(0x5000, 0, &this->memory_, &name, &func_offset));
+}
+
+TYPED_TEST_P(SymbolsTest, multiple_entries) {
+ Symbols symbols(0x1000, sizeof(TypeParam) * 3, sizeof(TypeParam), 0x2000, 0x500);
+
+ TypeParam sym;
+ uint64_t offset = 0x1000;
+ std::string fake_name;
+
+ this->InitSym(&sym, 0x5000, 0x10, 0x40);
+ this->memory_.SetMemory(offset, &sym, sizeof(sym));
+ fake_name = "function_one";
+ this->memory_.SetMemory(0x2040, fake_name.c_str(), fake_name.size() + 1);
+ offset += sizeof(sym);
+
+ this->InitSym(&sym, 0x3004, 0x200, 0x100);
+ this->memory_.SetMemory(offset, &sym, sizeof(sym));
+ fake_name = "function_two";
+ this->memory_.SetMemory(0x2100, fake_name.c_str(), fake_name.size() + 1);
+ offset += sizeof(sym);
+
+ this->InitSym(&sym, 0xa010, 0x20, 0x230);
+ this->memory_.SetMemory(offset, &sym, sizeof(sym));
+ fake_name = "function_three";
+ this->memory_.SetMemory(0x2230, fake_name.c_str(), fake_name.size() + 1);
+
+ std::string name;
+ uint64_t func_offset;
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0x3005, 0, &this->memory_, &name, &func_offset));
+ ASSERT_EQ("function_two", name);
+ ASSERT_EQ(1U, func_offset);
+
+ name.clear();
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0x5004, 0, &this->memory_, &name, &func_offset));
+ ASSERT_EQ("function_one", name);
+ ASSERT_EQ(4U, func_offset);
+
+ name.clear();
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0xa011, 0, &this->memory_, &name, &func_offset));
+ ASSERT_EQ("function_three", name);
+ ASSERT_EQ(1U, func_offset);
+
+ // Reget some of the others to verify getting one function name doesn't
+ // affect any of the next calls.
+ name.clear();
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0x5008, 0, &this->memory_, &name, &func_offset));
+ ASSERT_EQ("function_one", name);
+ ASSERT_EQ(8U, func_offset);
+
+ name.clear();
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0x3008, 0, &this->memory_, &name, &func_offset));
+ ASSERT_EQ("function_two", name);
+ ASSERT_EQ(4U, func_offset);
+
+ name.clear();
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0xa01a, 0, &this->memory_, &name, &func_offset));
+ ASSERT_EQ("function_three", name);
+ ASSERT_EQ(0xaU, func_offset);
+}
+
+TYPED_TEST_P(SymbolsTest, multiple_entries_nonstandard_size) {
+ uint64_t entry_size = sizeof(TypeParam) + 5;
+ Symbols symbols(0x1000, entry_size * 3, entry_size, 0x2000, 0x500);
+
+ TypeParam sym;
+ uint64_t offset = 0x1000;
+ std::string fake_name;
+
+ this->InitSym(&sym, 0x5000, 0x10, 0x40);
+ this->memory_.SetMemory(offset, &sym, sizeof(sym));
+ fake_name = "function_one";
+ this->memory_.SetMemory(0x2040, fake_name.c_str(), fake_name.size() + 1);
+ offset += entry_size;
+
+ this->InitSym(&sym, 0x3004, 0x200, 0x100);
+ this->memory_.SetMemory(offset, &sym, sizeof(sym));
+ fake_name = "function_two";
+ this->memory_.SetMemory(0x2100, fake_name.c_str(), fake_name.size() + 1);
+ offset += entry_size;
+
+ this->InitSym(&sym, 0xa010, 0x20, 0x230);
+ this->memory_.SetMemory(offset, &sym, sizeof(sym));
+ fake_name = "function_three";
+ this->memory_.SetMemory(0x2230, fake_name.c_str(), fake_name.size() + 1);
+
+ std::string name;
+ uint64_t func_offset;
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0x3005, 0, &this->memory_, &name, &func_offset));
+ ASSERT_EQ("function_two", name);
+ ASSERT_EQ(1U, func_offset);
+
+ name.clear();
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0x5004, 0, &this->memory_, &name, &func_offset));
+ ASSERT_EQ("function_one", name);
+ ASSERT_EQ(4U, func_offset);
+
+ name.clear();
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0xa011, 0, &this->memory_, &name, &func_offset));
+ ASSERT_EQ("function_three", name);
+ ASSERT_EQ(1U, func_offset);
+}
+
+TYPED_TEST_P(SymbolsTest, load_bias) {
+ Symbols symbols(0x1000, sizeof(TypeParam), sizeof(TypeParam), 0x2000, 0x100);
+
+ TypeParam sym;
+ this->InitSym(&sym, 0x5000, 0x10, 0x40);
+ uint64_t offset = 0x1000;
+ this->memory_.SetMemory(offset, &sym, sizeof(sym));
+
+ std::string fake_name("fake_function");
+ this->memory_.SetMemory(0x2040, fake_name.c_str(), fake_name.size() + 1);
+
+ // Set a non-zero load_bias that should be a valid function offset.
+ std::string name;
+ uint64_t func_offset;
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0x5004, 0x1000, &this->memory_, &name, &func_offset));
+ ASSERT_EQ("fake_function", name);
+ ASSERT_EQ(4U, func_offset);
+
+ // Set a flag that should cause the load_bias to be ignored.
+ sym.st_shndx = SHN_ABS;
+ this->memory_.SetMemory(offset, &sym, sizeof(sym));
+ // Clear the cache to force the symbol data to be re-read.
+ symbols.ClearCache();
+ ASSERT_FALSE(symbols.GetName<TypeParam>(0x5004, 0x1000, &this->memory_, &name, &func_offset));
+}
+
+TYPED_TEST_P(SymbolsTest, symtab_value_out_of_bounds) {
+ Symbols symbols_end_at_100(0x1000, sizeof(TypeParam) * 2, sizeof(TypeParam), 0x2000, 0x100);
+ Symbols symbols_end_at_200(0x1000, sizeof(TypeParam) * 2, sizeof(TypeParam), 0x2000, 0x200);
+
+ TypeParam sym;
+ uint64_t offset = 0x1000;
+
+ this->InitSym(&sym, 0x5000, 0x10, 0xfb);
+ this->memory_.SetMemory(offset, &sym, sizeof(sym));
+ offset += sizeof(sym);
+
+ this->InitSym(&sym, 0x3000, 0x10, 0x100);
+ this->memory_.SetMemory(offset, &sym, sizeof(sym));
+
+ // Put the name across the end of the tab.
+ std::string fake_name("fake_function");
+ this->memory_.SetMemory(0x20fb, fake_name.c_str(), fake_name.size() + 1);
+
+ std::string name;
+ uint64_t func_offset;
+ // Verify that we can get the function name properly for both entries.
+ ASSERT_TRUE(symbols_end_at_200.GetName<TypeParam>(0x5000, 0, &this->memory_, &name, &func_offset));
+ ASSERT_EQ("fake_function", name);
+ ASSERT_EQ(0U, func_offset);
+ ASSERT_TRUE(symbols_end_at_200.GetName<TypeParam>(0x3000, 0, &this->memory_, &name, &func_offset));
+ ASSERT_EQ("function", name);
+ ASSERT_EQ(0U, func_offset);
+
+ // Now use the symbol table that ends at 0x100.
+ ASSERT_FALSE(
+ symbols_end_at_100.GetName<TypeParam>(0x5000, 0, &this->memory_, &name, &func_offset));
+ ASSERT_FALSE(
+ symbols_end_at_100.GetName<TypeParam>(0x3000, 0, &this->memory_, &name, &func_offset));
+}
+
+// Verify the entire func table is cached.
+TYPED_TEST_P(SymbolsTest, symtab_read_cached) {
+ Symbols symbols(0x1000, 3 * sizeof(TypeParam), sizeof(TypeParam), 0xa000, 0x1000);
+
+ TypeParam sym;
+ uint64_t offset = 0x1000;
+
+ // Make sure that these entries are not in ascending order.
+ this->InitSym(&sym, 0x5000, 0x10, 0x100);
+ this->memory_.SetMemory(offset, &sym, sizeof(sym));
+ offset += sizeof(sym);
+
+ this->InitSym(&sym, 0x2000, 0x300, 0x200);
+ this->memory_.SetMemory(offset, &sym, sizeof(sym));
+ offset += sizeof(sym);
+
+ this->InitSym(&sym, 0x1000, 0x100, 0x300);
+ this->memory_.SetMemory(offset, &sym, sizeof(sym));
+ offset += sizeof(sym);
+
+ // Do call that should cache all of the entries (except the string data).
+ std::string name;
+ uint64_t func_offset;
+ ASSERT_FALSE(symbols.GetName<TypeParam>(0x6000, 0, &this->memory_, &name, &func_offset));
+ this->memory_.Clear();
+ ASSERT_FALSE(symbols.GetName<TypeParam>(0x6000, 0, &this->memory_, &name, &func_offset));
+
+ // Clear the memory and only put the symbol data string data in memory.
+ this->memory_.Clear();
+
+ std::string fake_name;
+ fake_name = "first_entry";
+ this->memory_.SetMemory(0xa100, fake_name.c_str(), fake_name.size() + 1);
+ fake_name = "second_entry";
+ this->memory_.SetMemory(0xa200, fake_name.c_str(), fake_name.size() + 1);
+ fake_name = "third_entry";
+ this->memory_.SetMemory(0xa300, fake_name.c_str(), fake_name.size() + 1);
+
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0x5001, 0, &this->memory_, &name, &func_offset));
+ ASSERT_EQ("first_entry", name);
+ ASSERT_EQ(1U, func_offset);
+
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0x2002, 0, &this->memory_, &name, &func_offset));
+ ASSERT_EQ("second_entry", name);
+ ASSERT_EQ(2U, func_offset);
+
+ ASSERT_TRUE(symbols.GetName<TypeParam>(0x1003, 0, &this->memory_, &name, &func_offset));
+ ASSERT_EQ("third_entry", name);
+ ASSERT_EQ(3U, func_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);
+
+typedef ::testing::Types<Elf32_Sym, Elf64_Sym> SymbolsTestTypes;
+INSTANTIATE_TYPED_TEST_CASE_P(, SymbolsTest, SymbolsTestTypes);
diff --git a/libutils/include/utils/Compat.h b/libutils/include/utils/Compat.h
index 2709e3b..dee577e 100644
--- a/libutils/include/utils/Compat.h
+++ b/libutils/include/utils/Compat.h
@@ -37,6 +37,10 @@
return pwrite(fd, buf, nbytes, offset);
}
+static inline int ftruncate64(int fd, off64_t length) {
+ return ftruncate(fd, length);
+}
+
#endif /* __APPLE__ */
#if defined(_WIN32)
diff --git a/libutils/include/utils/FastStrcmp.h b/libutils/include/utils/FastStrcmp.h
index 3844e7d..5cadc94 100644
--- a/libutils/include/utils/FastStrcmp.h
+++ b/libutils/include/utils/FastStrcmp.h
@@ -17,6 +17,13 @@
#ifndef _ANDROID_UTILS_FASTSTRCMP_H__
#define _ANDROID_UTILS_FASTSTRCMP_H__
+#include <ctype.h>
+#include <string.h>
+
+#ifndef __predict_true
+#define __predict_true(exp) __builtin_expect((exp) != 0, 1)
+#endif
+
#ifdef __cplusplus
// Optimized for instruction cache locality
@@ -28,25 +35,41 @@
//
// fastcmp<strncmp>(str1, str2, len)
//
-// NB: Does not work for the case insensitive str*cmp functions.
+// NB: use fasticmp for the case insensitive str*cmp functions.
// NB: Returns boolean, do not use if expecting to check negative value.
// Thus not semantically identical to the expected function behavior.
-template <int (*cmp)(const char *l, const char *r, const size_t s)>
-static inline int fastcmp(const char *l, const char *r, const size_t s) {
- return (*l != *r) || cmp(l + 1, r + 1, s - 1);
+template <int (*cmp)(const char* l, const char* r, const size_t s)>
+static inline int fastcmp(const char* l, const char* r, const size_t s) {
+ const ssize_t n = s; // To help reject negative sizes, treat like zero
+ return __predict_true(n > 0) &&
+ ((*l != *r) || (__predict_true(n > 1) && cmp(l + 1, r + 1, n - 1)));
}
-template <int (*cmp)(const void *l, const void *r, const size_t s)>
-static inline int fastcmp(const void *lv, const void *rv, const size_t s) {
- const char *l = static_cast<const char *>(lv);
- const char *r = static_cast<const char *>(rv);
- return (*l != *r) || cmp(l + 1, r + 1, s - 1);
+template <int (*cmp)(const char* l, const char* r, const size_t s)>
+static inline int fasticmp(const char* l, const char* r, const size_t s) {
+ const ssize_t n = s; // To help reject negative sizes, treat like zero
+ return __predict_true(n > 0) &&
+ ((tolower(*l) != tolower(*r)) || (__predict_true(n > 1) && cmp(l + 1, r + 1, n - 1)));
}
-template <int (*cmp)(const char *l, const char *r)>
-static inline int fastcmp(const char *l, const char *r) {
- return (*l != *r) || cmp(l + 1, r + 1);
+template <int (*cmp)(const void* l, const void* r, const size_t s)>
+static inline int fastcmp(const void* lv, const void* rv, const size_t s) {
+ const char* l = static_cast<const char*>(lv);
+ const char* r = static_cast<const char*>(rv);
+ const ssize_t n = s; // To help reject negative sizes, treat like zero
+ return __predict_true(n > 0) &&
+ ((*l != *r) || (__predict_true(n > 1) && cmp(l + 1, r + 1, n - 1)));
+}
+
+template <int (*cmp)(const char* l, const char* r)>
+static inline int fastcmp(const char* l, const char* r) {
+ return (*l != *r) || (__predict_true(*l) && cmp(l + 1, r + 1));
+}
+
+template <int (*cmp)(const char* l, const char* r)>
+static inline int fasticmp(const char* l, const char* r) {
+ return (tolower(*l) != tolower(*r)) || (__predict_true(*l) && cmp(l + 1, r + 1));
}
#endif
diff --git a/libziparchive/zip_writer.cc b/libziparchive/zip_writer.cc
index cbc86b8..2edf224 100644
--- a/libziparchive/zip_writer.cc
+++ b/libziparchive/zip_writer.cc
@@ -26,6 +26,7 @@
#include <vector>
#include "android-base/logging.h"
+#include "utils/Compat.h"
#include "utils/Log.h"
#include "entry_name_utils-inl.h"
diff --git a/libziparchive/zip_writer_test.cc b/libziparchive/zip_writer_test.cc
index db77aa4..5b526a4 100644
--- a/libziparchive/zip_writer_test.cc
+++ b/libziparchive/zip_writer_test.cc
@@ -378,7 +378,7 @@
ASSERT_EQ(0, writer.WriteBytes(data.data(), data.size()));
ASSERT_EQ(0, writer.FinishEntry());
- off64_t before_len = ftello64(file_);
+ off_t before_len = ftello(file_);
ZipWriter::FileEntry entry;
ASSERT_EQ(0, writer.GetLastEntry(&entry));
@@ -386,7 +386,7 @@
ASSERT_EQ(0, writer.Finish());
- off64_t after_len = ftello64(file_);
+ off_t after_len = ftello(file_);
ASSERT_GT(before_len, after_len);
}
diff --git a/logcat/Android.mk b/logcat/Android.mk
index f564f0f..4e11ca9 100644
--- a/logcat/Android.mk
+++ b/logcat/Android.mk
@@ -15,6 +15,16 @@
include $(CLEAR_VARS)
+LOCAL_MODULE := logcatd
+LOCAL_MODULE_TAGS := debug
+LOCAL_SRC_FILES := logcatd_main.cpp event.logtags
+LOCAL_SHARED_LIBRARIES := liblogcat $(logcatLibs)
+LOCAL_CFLAGS := -Werror
+
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+
LOCAL_MODULE := liblogcat
LOCAL_SRC_FILES := logcat.cpp getopt_long.cpp logcat_system.cpp
LOCAL_SHARED_LIBRARIES := $(logcatLibs)
diff --git a/logcat/event.logtags b/logcat/event.logtags
index 909f8e2..9f05351 100644
--- a/logcat/event.logtags
+++ b/logcat/event.logtags
@@ -36,6 +36,8 @@
# These are used for testing, do not modify without updating
# tests/framework-tests/src/android/util/EventLogFunctionalTest.java.
+# system/core/liblog/tests/liblog_benchmark.cpp
+# system/core/liblog/tests/liblog_test.cpp
42 answer (to life the universe etc|3)
314 pi
2718 e
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index 8134936..64d1d2f 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -882,6 +882,7 @@
{ "grep", required_argument, nullptr, 'e' },
// hidden and undocumented reserved alias for --max-count
{ "head", required_argument, nullptr, 'm' },
+ { "help", no_argument, nullptr, 'h' },
{ id_str, required_argument, nullptr, 0 },
{ "last", no_argument, nullptr, 'L' },
{ "max-count", required_argument, nullptr, 'm' },
@@ -900,9 +901,8 @@
};
// clang-format on
- ret = getopt_long_r(argc, argv,
- ":cdDLt:T:gG:sQf:r:n:v:b:BSpP:m:e:", long_options,
- &option_index, &optctx);
+ ret = getopt_long_r(argc, argv, ":cdDhLt:T:gG:sQf:r:n:v:b:BSpP:m:e:",
+ long_options, &option_index, &optctx);
if (ret < 0) break;
switch (ret) {
@@ -1304,6 +1304,11 @@
"Option -%c needs an argument\n", optctx.optopt);
goto exit;
+ case 'h':
+ show_help(context);
+ show_format_help(context);
+ goto exit;
+
default:
logcat_panic(context, HELP_TRUE, "Unrecognized Option %c\n",
optctx.optopt);
diff --git a/logcat/logcatd.rc b/logcat/logcatd.rc
index b082a64..06cc90d 100644
--- a/logcat/logcatd.rc
+++ b/logcat/logcatd.rc
@@ -34,9 +34,6 @@
on property:logd.logpersistd.enable=true && property:logd.logpersistd=logcatd
# all exec/services are called with umask(077), so no gain beyond 0700
mkdir /data/misc/logd 0700 logd log
- # logd for write to /data/misc/logd, log group for read from pstore (-L)
- # b/28788401 b/30041146 b/30612424
- # exec - logd log -- /system/bin/logcat -L -b ${logd.logpersistd.buffer:-all} -v threadtime -v usec -v printable -D -f /data/misc/logd/logcat -r 1024 -n ${logd.logpersistd.size:-256} --id=${ro.build.id}
start logcatd
# stop logcatd service and clear data
@@ -57,7 +54,7 @@
stop logcatd
# logcatd service
-service logcatd /system/bin/logcat -b ${logd.logpersistd.buffer:-all} -v threadtime -v usec -v printable -D -f /data/misc/logd/logcat -r 1024 -n ${logd.logpersistd.size:-256} --id=${ro.build.id}
+service logcatd /system/bin/logcatd -L -b ${logd.logpersistd.buffer:-all} -v threadtime -v usec -v printable -D -f /data/misc/logd/logcat -r 1024 -n ${logd.logpersistd.size:-256} --id=${ro.build.id}
class late_start
disabled
# logd for write to /data/misc/logd, log group for read from log daemon
diff --git a/logcat/logcatd_main.cpp b/logcat/logcatd_main.cpp
new file mode 100644
index 0000000..9109eb1
--- /dev/null
+++ b/logcat/logcatd_main.cpp
@@ -0,0 +1,71 @@
+/*
+ * 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 <signal.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <string>
+#include <vector>
+
+#include <log/logcat.h>
+
+int main(int argc, char** argv, char** envp) {
+ android_logcat_context ctx = create_android_logcat();
+ if (!ctx) return -1;
+
+ signal(SIGPIPE, exit);
+
+ // Save and detect presence of -L or --last flag
+ std::vector<std::string> args;
+ bool last = false;
+ for (int i = 0; i < argc; ++i) {
+ if (!argv[i]) continue;
+ args.push_back(std::string(argv[i]));
+ if (!strcmp(argv[i], "-L") || !strcmp(argv[i], "--last")) last = true;
+ }
+
+ // Generate argv from saved content
+ std::vector<const char*> argv_hold;
+ for (auto& str : args) argv_hold.push_back(str.c_str());
+ argv_hold.push_back(nullptr);
+
+ int ret = 0;
+ if (last) {
+ // Run logcat command with -L flag
+ ret = android_logcat_run_command(ctx, -1, -1, argv_hold.size() - 1,
+ (char* const*)&argv_hold[0], envp);
+ // Remove -L and --last flags from argument list
+ for (std::vector<const char*>::iterator it = argv_hold.begin();
+ it != argv_hold.end();) {
+ if (!*it || (strcmp(*it, "-L") && strcmp(*it, "--last"))) {
+ ++it;
+ } else {
+ it = argv_hold.erase(it);
+ }
+ }
+ // fall through to re-run the command regardless of the arguments
+ // passed in. For instance, we expect -h to report help stutter.
+ }
+
+ // Run logcat command without -L flag
+ int retval = android_logcat_run_command(ctx, -1, -1, argv_hold.size() - 1,
+ (char* const*)&argv_hold[0], envp);
+ if (!ret) ret = retval;
+ retval = android_logcat_destroy(&ctx);
+ if (!ret) ret = retval;
+ return ret;
+}
diff --git a/logcat/tests/Android.mk b/logcat/tests/Android.mk
index 22aca17..defd3c4 100644
--- a/logcat/tests/Android.mk
+++ b/logcat/tests/Android.mk
@@ -50,6 +50,7 @@
test_src_files := \
logcat_test.cpp \
+ logcatd_test.cpp \
liblogcat_test.cpp \
# Build tests for the device (with .so). Run with:
diff --git a/logcat/tests/logcat_test.cpp b/logcat/tests/logcat_test.cpp
index 0895834..21868f2 100644
--- a/logcat/tests/logcat_test.cpp
+++ b/logcat/tests/logcat_test.cpp
@@ -20,6 +20,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/cdefs.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
@@ -39,6 +40,10 @@
#define logcat_pclose(context, fp) pclose(fp)
#define logcat_system(command) system(command)
#endif
+#ifndef logcat_executable
+#define USING_LOGCAT_EXECUTABLE_DEFAULT
+#define logcat_executable "logcat"
+#endif
#define BIG_BUFFER (5 * 1024)
@@ -64,14 +69,13 @@
#undef LOG_TAG
#define LOG_TAG "inject"
- RLOGE("logcat.buckets");
+ RLOGE(logcat_executable ".buckets");
sleep(1);
- ASSERT_TRUE(
- NULL !=
- (fp = logcat_popen(
- ctx,
- "logcat -b radio -b events -b system -b main -d 2>/dev/null")));
+ ASSERT_TRUE(NULL !=
+ (fp = logcat_popen(
+ ctx, logcat_executable
+ " -b radio -b events -b system -b main -d 2>/dev/null")));
char buffer[BIG_BUFFER];
@@ -101,8 +105,8 @@
logcat_define(ctx);
ASSERT_TRUE(NULL !=
- (fp = logcat_popen(ctx,
- "logcat -b events -d -s auditd "
+ (fp = logcat_popen(ctx, logcat_executable
+ " -b events -d -s auditd "
"am_proc_start am_pss am_proc_bound "
"dvm_lock_sample am_wtf 2>/dev/null")));
@@ -170,10 +174,9 @@
#endif
strftime(needle, sizeof(needle), "[ %Y-", ptm);
- ASSERT_TRUE(
- NULL !=
- (fp = logcat_popen(
- ctx, "logcat -v long -v year -b all -t 3 2>/dev/null")));
+ ASSERT_TRUE(NULL != (fp = logcat_popen(
+ ctx, logcat_executable
+ " -v long -v year -b all -t 3 2>/dev/null")));
char buffer[BIG_BUFFER];
@@ -237,8 +240,8 @@
logcat_define(ctx);
ASSERT_TRUE(NULL !=
- (fp = logcat_popen(ctx,
- "logcat -v long -v America/Los_Angeles "
+ (fp = logcat_popen(ctx, logcat_executable
+ " -v long -v America/Los_Angeles "
"-b all -t 3 2>/dev/null")));
char buffer[BIG_BUFFER];
@@ -264,10 +267,9 @@
FILE* fp;
logcat_define(ctx);
- ASSERT_TRUE(NULL !=
- (fp = logcat_popen(ctx,
- "logcat -v long -v America/Los_Angeles -v "
- "zone -b all -t 3 2>/dev/null")));
+ ASSERT_TRUE(NULL != (fp = logcat_popen(ctx, logcat_executable
+ " -v long -v America/Los_Angeles -v "
+ "zone -b all -t 3 2>/dev/null")));
char buffer[BIG_BUFFER];
@@ -435,11 +437,11 @@
}
TEST(logcat, tail_time) {
- do_tail_time("logcat -v long -v nsec -b all");
+ do_tail_time(logcat_executable " -v long -v nsec -b all");
}
TEST(logcat, tail_time_epoch) {
- do_tail_time("logcat -v long -v nsec -v epoch -b all");
+ do_tail_time(logcat_executable " -v long -v nsec -v epoch -b all");
}
TEST(logcat, End_to_End) {
@@ -452,8 +454,8 @@
FILE* fp;
logcat_define(ctx);
ASSERT_TRUE(NULL !=
- (fp = logcat_popen(
- ctx, "logcat -v brief -b events -t 100 2>/dev/null")));
+ (fp = logcat_popen(ctx, logcat_executable
+ " -v brief -b events -t 100 2>/dev/null")));
char buffer[BIG_BUFFER];
@@ -479,6 +481,56 @@
ASSERT_EQ(1, count);
}
+TEST(logcat, End_to_End_multitude) {
+ pid_t pid = getpid();
+
+ log_time ts(CLOCK_MONOTONIC);
+
+ ASSERT_LT(0, __android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts)));
+
+ FILE* fp[256]; // does this count as a multitude!
+ memset(fp, 0, sizeof(fp));
+ logcat_define(ctx[sizeof(fp) / sizeof(fp[0])]);
+ size_t num = 0;
+ do {
+ EXPECT_TRUE(NULL !=
+ (fp[num] = logcat_popen(ctx[num], logcat_executable
+ " -v brief -b events -t 100")));
+ if (!fp[num]) {
+ fprintf(stderr,
+ "WARNING: limiting to %zu simultaneous logcat operations\n",
+ num);
+ break;
+ }
+ } while (++num < sizeof(fp) / sizeof(fp[0]));
+
+ char buffer[BIG_BUFFER];
+
+ size_t count = 0;
+
+ for (size_t idx = 0; idx < sizeof(fp) / sizeof(fp[0]); ++idx) {
+ if (!fp[idx]) break;
+ while (fgets(buffer, sizeof(buffer), fp[idx])) {
+ int p;
+ unsigned long long t;
+
+ if ((2 != sscanf(buffer, "I/[0] ( %d): %llu", &p, &t)) ||
+ (p != pid)) {
+ continue;
+ }
+
+ log_time tx((const char*)&t);
+ if (ts == tx) {
+ ++count;
+ }
+ }
+
+ logcat_pclose(ctx[idx], fp[idx]);
+ }
+
+ ASSERT_EQ(num, count);
+}
+
static int get_groups(const char* cmd) {
FILE* fp;
logcat_define(ctx);
@@ -554,22 +606,23 @@
}
TEST(logcat, get_size) {
- ASSERT_EQ(4, get_groups("logcat -v brief -b radio -b events -b system -b "
+ ASSERT_EQ(4, get_groups(logcat_executable
+ " -v brief -b radio -b events -b system -b "
"main -g 2>/dev/null"));
}
// duplicate test for get_size, but use comma-separated list of buffers
TEST(logcat, multiple_buffer) {
ASSERT_EQ(
- 4, get_groups(
- "logcat -v brief -b radio,events,system,main -g 2>/dev/null"));
+ 4, get_groups(logcat_executable
+ " -v brief -b radio,events,system,main -g 2>/dev/null"));
}
TEST(logcat, bad_buffer) {
- ASSERT_EQ(
- 0,
- get_groups(
- "logcat -v brief -b radio,events,bogo,system,main -g 2>/dev/null"));
+ ASSERT_EQ(0,
+ get_groups(
+ logcat_executable
+ " -v brief -b radio,events,bogo,system,main -g 2>/dev/null"));
}
#ifndef logcat
@@ -724,8 +777,8 @@
char buf[sizeof(form)];
ASSERT_TRUE(NULL != mkdtemp(strcpy(buf, form)));
- static const char comm[] =
- "logcat -b radio -b events -b system -b main"
+ static const char comm[] = logcat_executable
+ " -b radio -b events -b system -b main"
" -d -f %s/log.txt -n 7 -r 1";
char command[sizeof(buf) + sizeof(comm)];
snprintf(command, sizeof(command), comm, buf);
@@ -770,8 +823,8 @@
char tmp_out_dir[sizeof(tmp_out_dir_form)];
ASSERT_TRUE(NULL != mkdtemp(strcpy(tmp_out_dir, tmp_out_dir_form)));
- static const char logcat_cmd[] =
- "logcat -b radio -b events -b system -b main"
+ static const char logcat_cmd[] = logcat_executable
+ " -b radio -b events -b system -b main"
" -d -f %s/log.txt -n 10 -r 1";
char command[sizeof(tmp_out_dir) + sizeof(logcat_cmd)];
snprintf(command, sizeof(command), logcat_cmd, tmp_out_dir);
@@ -830,7 +883,7 @@
static const char log_filename[] = "log.txt";
static const char logcat_cmd[] =
- "logcat -b all -v nsec -d -f %s/%s -n 256 -r 1024";
+ logcat_executable " -b all -v nsec -d -f %s/%s -n 256 -r 1024";
static const char cleanup_cmd[] = "rm -rf %s";
char command[sizeof(tmp_out_dir) + sizeof(logcat_cmd) + sizeof(log_filename)];
snprintf(command, sizeof(command), logcat_cmd, tmp_out_dir, log_filename);
@@ -955,7 +1008,8 @@
static const char log_filename[] = "log.txt";
static const unsigned num_val = 32;
- static const char logcat_cmd[] = "logcat -b all -d -f %s/%s -n %d -r 1";
+ static const char logcat_cmd[] =
+ logcat_executable " -b all -d -f %s/%s -n %d -r 1";
static const char clear_cmd[] = " -c";
static const char cleanup_cmd[] = "rm -rf %s";
char command[sizeof(tmp_out_dir) + sizeof(logcat_cmd) +
@@ -1059,9 +1113,9 @@
TEST(logcat, logrotate_id) {
static const char logcat_cmd[] =
- "logcat -b all -d -f %s/%s -n 32 -r 1 --id=test";
+ logcat_executable " -b all -d -f %s/%s -n 32 -r 1 --id=test";
static const char logcat_short_cmd[] =
- "logcat -b all -t 10 -f %s/%s -n 32 -r 1 --id=test";
+ logcat_executable " -b all -t 10 -f %s/%s -n 32 -r 1 --id=test";
static const char tmp_out_dir_form[] =
"/data/local/tmp/logcat.logrotate.XXXXXX";
static const char log_filename[] = "log.txt";
@@ -1105,8 +1159,8 @@
TEST(logcat, logrotate_nodir) {
// expect logcat to error out on writing content and not exit(0) for nodir
- static const char command[] =
- "logcat -b all -d"
+ static const char command[] = logcat_executable
+ " -b all -d"
" -f /das/nein/gerfingerpoken/logcat/log.txt"
" -n 256 -r 1024";
EXPECT_FALSE(IsFalse(0 == logcat_system(command), command));
@@ -1242,7 +1296,7 @@
FILE* fp;
logcat_define(ctx);
- fp = logcat_popen(ctx, "logcat -p 2>/dev/null");
+ fp = logcat_popen(ctx, logcat_executable " -p 2>/dev/null");
if (fp == NULL) {
fprintf(stderr, "ERROR: logcat -p 2>/dev/null\n");
return false;
@@ -1280,7 +1334,8 @@
char buffer[BIG_BUFFER];
- snprintf(buffer, sizeof(buffer), "logcat -P '%s' 2>&1", list ? list : "");
+ snprintf(buffer, sizeof(buffer), logcat_executable " -P '%s' 2>&1",
+ list ? list : "");
fp = logcat_popen(ctx, buffer);
if (fp == NULL) {
fprintf(stderr, "ERROR: %s\n", buffer);
@@ -1342,15 +1397,11 @@
int count = 0;
char buffer[BIG_BUFFER];
-// Have to make liblogcat data unique from logcat data injection
-#ifdef logcat
-#define logcat_regex_prefix "lolcat_test"
-#else
-#define logcat_regex_prefix "logcat_test"
-#endif
+#define logcat_regex_prefix ___STRING(logcat) "_test"
snprintf(buffer, sizeof(buffer),
- "logcat --pid %d -d -e " logcat_regex_prefix "_a+b", getpid());
+ logcat_executable " --pid %d -d -e " logcat_regex_prefix "_a+b",
+ getpid());
LOG_FAILURE_RETRY(__android_log_print(ANDROID_LOG_WARN, logcat_regex_prefix,
logcat_regex_prefix "_ab"));
@@ -1387,8 +1438,8 @@
char buffer[BIG_BUFFER];
- snprintf(buffer, sizeof(buffer), "logcat --pid %d -d --max-count 3",
- getpid());
+ snprintf(buffer, sizeof(buffer),
+ logcat_executable " --pid %d -d --max-count 3", getpid());
LOG_FAILURE_RETRY(
__android_log_print(ANDROID_LOG_WARN, "logcat_test", "logcat_test"));
@@ -1613,8 +1664,34 @@
}
TEST(logcat, security) {
- EXPECT_FALSE(reportedSecurity("logcat -b all -g 2>&1"));
- EXPECT_TRUE(reportedSecurity("logcat -b security -g 2>&1"));
- EXPECT_TRUE(reportedSecurity("logcat -b security -c 2>&1"));
- EXPECT_TRUE(reportedSecurity("logcat -b security -G 256K 2>&1"));
+ EXPECT_FALSE(reportedSecurity(logcat_executable " -b all -g 2>&1"));
+ EXPECT_TRUE(reportedSecurity(logcat_executable " -b security -g 2>&1"));
+ EXPECT_TRUE(reportedSecurity(logcat_executable " -b security -c 2>&1"));
+ EXPECT_TRUE(
+ reportedSecurity(logcat_executable " -b security -G 256K 2>&1"));
+}
+
+static size_t commandOutputSize(const char* command) {
+ logcat_define(ctx);
+ FILE* fp = logcat_popen(ctx, command);
+ if (!fp) return 0;
+
+ std::string ret;
+ if (!android::base::ReadFdToString(fileno(fp), &ret)) return 0;
+ if (logcat_pclose(ctx, fp) != 0) return 0;
+
+ return ret.size();
+}
+
+TEST(logcat, help) {
+ size_t logcatHelpTextSize = commandOutputSize(logcat_executable " -h 2>&1");
+ EXPECT_LT(4096UL, logcatHelpTextSize);
+ size_t logcatLastHelpTextSize =
+ commandOutputSize(logcat_executable " -L -h 2>&1");
+#ifdef USING_LOGCAT_EXECUTABLE_DEFAULT // logcat and liblogcat
+ EXPECT_EQ(logcatHelpTextSize, logcatLastHelpTextSize);
+#else
+ // logcatd -L -h prints the help twice, as designed.
+ EXPECT_EQ(logcatHelpTextSize * 2, logcatLastHelpTextSize);
+#endif
}
diff --git a/bootstat/uptime_parser.h b/logcat/tests/logcatd_test.cpp
similarity index 65%
rename from bootstat/uptime_parser.h
rename to logcat/tests/logcatd_test.cpp
index 756ae9b..bb7534e 100644
--- a/bootstat/uptime_parser.h
+++ b/logcat/tests/logcatd_test.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2016 The Android Open Source Project
+ * 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.
@@ -14,16 +14,7 @@
* limitations under the License.
*/
-#ifndef UPTIME_PARSER_H_
-#define UPTIME_PARSER_H_
+#define logcat logcatd
+#define logcat_executable "logcatd"
-#include <time.h>
-
-namespace bootstat {
-
-// Returns the number of seconds the system has been on since reboot.
-time_t ParseUptime();
-
-} // namespace bootstat
-
-#endif // UPTIME_PARSER_H_
\ No newline at end of file
+#include "logcat_test.cpp"
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
index 352fc18..0c7019a 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -109,11 +109,11 @@
LogBuffer::LogBuffer(LastLogTimes* times)
: monotonic(android_log_clockid() == CLOCK_MONOTONIC), mTimes(*times) {
- pthread_mutex_init(&mLogElementsLock, NULL);
+ pthread_mutex_init(&mLogElementsLock, nullptr);
log_id_for_each(i) {
- lastLoggedElements[i] = NULL;
- droppedElements[i] = NULL;
+ lastLoggedElements[i] = nullptr;
+ droppedElements[i] = nullptr;
}
init();
@@ -132,11 +132,11 @@
LogBufferElement* last) {
// is it mostly identical?
// if (!elem) return DIFFERENT;
- unsigned short lenl = elem->getMsgLen();
- if (!lenl) return DIFFERENT;
+ ssize_t lenl = elem->getMsgLen();
+ if (lenl <= 0) return DIFFERENT; // value if this represents a chatty elem
// if (!last) return DIFFERENT;
- unsigned short lenr = last->getMsgLen();
- if (!lenr) return DIFFERENT;
+ ssize_t lenr = last->getMsgLen();
+ if (lenr <= 0) return DIFFERENT; // value if this represents a chatty elem
// if (elem->getLogId() != last->getLogId()) return DIFFERENT;
if (elem->getUid() != last->getUid()) return DIFFERENT;
if (elem->getPid() != last->getPid()) return DIFFERENT;
@@ -162,8 +162,6 @@
}
// audit message (except sequence number) identical?
- static const char avc[] = "): avc: ";
-
if (last->isBinary()) {
if (fastcmp<memcmp>(msgl, msgr, sizeof(android_log_event_string_t) -
sizeof(int32_t)))
@@ -173,6 +171,7 @@
msgr += sizeof(android_log_event_string_t);
lenr -= sizeof(android_log_event_string_t);
}
+ static const char avc[] = "): avc: ";
const char* avcl = android::strnstr(msgl, lenl, avc);
if (!avcl) return DIFFERENT;
lenl -= avcl - msgl;
@@ -180,10 +179,7 @@
if (!avcr) return DIFFERENT;
lenr -= avcr - msgr;
if (lenl != lenr) return DIFFERENT;
- // TODO: After b/35468874 is addressed, revisit "lenl > strlen(avc)"
- // condition, it might become superfluous.
- if (lenl > strlen(avc) &&
- fastcmp<memcmp>(avcl + strlen(avc), avcr + strlen(avc),
+ if (fastcmp<memcmp>(avcl + strlen(avc), avcr + strlen(avc),
lenl - strlen(avc))) {
return DIFFERENT;
}
@@ -200,7 +196,7 @@
new LogBufferElement(log_id, realtime, uid, pid, tid, msg, len);
if (log_id != LOG_ID_SECURITY) {
int prio = ANDROID_LOG_INFO;
- const char* tag = NULL;
+ const char* tag = nullptr;
if (log_id == LOG_ID_EVENTS) {
tag = tagToName(elem->getTag());
} else {
@@ -226,24 +222,24 @@
//
// State Init
// incoming:
- // dropped = NULL
- // currentLast = NULL;
+ // dropped = nullptr
+ // currentLast = nullptr;
// elem = incoming message
// outgoing:
- // dropped = NULL -> State 0
+ // dropped = nullptr -> State 0
// currentLast = copy of elem
// log elem
// State 0
// incoming:
// count = 0
- // dropped = NULL
+ // dropped = nullptr
// currentLast = copy of last message
// elem = incoming message
// outgoing: if match != DIFFERENT
// dropped = copy of first identical message -> State 1
// currentLast = reference to elem
// break: if match == DIFFERENT
- // dropped = NULL -> State 0
+ // dropped = nullptr -> State 0
// delete copy of last message (incoming currentLast)
// currentLast = copy of elem
// log elem
@@ -270,7 +266,7 @@
// currentLast = reference to elem, sum liblog.
// break: if match == DIFFERENT
// delete dropped
- // dropped = NULL -> State 0
+ // dropped = nullptr -> State 0
// log reference to last held-back (currentLast)
// currentLast = copy of elem
// log elem
@@ -289,7 +285,7 @@
// currentLast = reference to elem
// break: if match == DIFFERENT
// log dropped (chatty message)
- // dropped = NULL -> State 0
+ // dropped = nullptr -> State 0
// log reference to last held-back (currentLast)
// currentLast = copy of elem
// log elem
@@ -354,7 +350,7 @@
} else { // State 1
delete dropped;
}
- droppedElements[log_id] = NULL;
+ droppedElements[log_id] = nullptr;
log(currentLast); // report last message in the series
} else { // State 0
delete currentLast;
@@ -658,7 +654,7 @@
// mLogElementsLock must be held when this function is called.
//
bool LogBuffer::prune(log_id_t id, unsigned long pruneRows, uid_t caller_uid) {
- LogTimeEntry* oldest = NULL;
+ LogTimeEntry* oldest = nullptr;
bool busy = false;
bool clearAll = pruneRows == ULONG_MAX;
@@ -1080,9 +1076,11 @@
return retval;
}
-log_time LogBuffer::flushTo(
- SocketClient* reader, const log_time& start, bool privileged, bool security,
- int (*filter)(const LogBufferElement* element, void* arg), void* arg) {
+log_time LogBuffer::flushTo(SocketClient* reader, const log_time& start,
+ pid_t* lastTid, bool privileged, bool security,
+ int (*filter)(const LogBufferElement* element,
+ void* arg),
+ void* arg) {
LogBufferElementCollection::iterator it;
uid_t uid = reader->getUid();
@@ -1092,18 +1090,20 @@
// client wants to start from the beginning
it = mLogElements.begin();
} else {
- LogBufferElementCollection::iterator last = mLogElements.begin();
- // 30 second limit to continue search for out-of-order entries.
- log_time min = start - log_time(30, 0);
+ LogBufferElementCollection::iterator last;
+ // 3 second limit to continue search for out-of-order entries.
+ log_time min = start - log_time(3, 0);
+ // Cap to 300 iterations we look back for out-of-order entries.
+ size_t count = 300;
// Client wants to start from some specified time. Chances are
// we are better off starting from the end of the time sorted list.
- for (it = mLogElements.end(); it != mLogElements.begin();
+ for (last = it = mLogElements.end(); it != mLogElements.begin();
/* do nothing */) {
--it;
LogBufferElement* element = *it;
if (element->getRealTime() > start) {
last = it;
- } else if (element->getRealTime() < min) {
+ } else if (!--count || (element->getRealTime() < min)) {
break;
}
}
@@ -1111,9 +1111,6 @@
}
log_time max = start;
- // Help detect if the valid message before is from the same source so
- // we can differentiate chatty filter types.
- pid_t lastTid[LOG_ID_MAX] = { 0 };
for (; it != mLogElements.end(); ++it) {
LogBufferElement* element = *it;
@@ -1141,14 +1138,17 @@
}
}
- bool sameTid = lastTid[element->getLogId()] == element->getTid();
- // Dropped (chatty) immediately following a valid log from the
- // same source in the same log buffer indicates we have a
- // multiple identical squash. chatty that differs source
- // is due to spam filter. chatty to chatty of different
- // source is also due to spam filter.
- lastTid[element->getLogId()] =
- (element->getDropped() && !sameTid) ? 0 : element->getTid();
+ bool sameTid = false;
+ if (lastTid) {
+ sameTid = lastTid[element->getLogId()] == element->getTid();
+ // Dropped (chatty) immediately following a valid log from the
+ // same source in the same log buffer indicates we have a
+ // multiple identical squash. chatty that differs source
+ // is due to spam filter. chatty to chatty of different
+ // source is also due to spam filter.
+ lastTid[element->getLogId()] =
+ (element->getDropped() && !sameTid) ? 0 : element->getTid();
+ }
pthread_mutex_unlock(&mLogElementsLock);
diff --git a/logd/LogBuffer.h b/logd/LogBuffer.h
index fcf6b9a..19d11cb 100644
--- a/logd/LogBuffer.h
+++ b/logd/LogBuffer.h
@@ -115,11 +115,15 @@
int log(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid, pid_t tid,
const char* msg, unsigned short len);
+ // lastTid is an optional context to help detect if the last previous
+ // valid message was from the same source so we can differentiate chatty
+ // filter types (identical or expired)
log_time flushTo(SocketClient* writer, const log_time& start,
+ pid_t* lastTid, // &lastTid[LOG_ID_MAX] or nullptr
bool privileged, bool security,
int (*filter)(const LogBufferElement* element,
- void* arg) = NULL,
- void* arg = NULL);
+ void* arg) = nullptr,
+ void* arg = nullptr);
bool clear(log_id_t id, uid_t uid = AID_ROOT);
unsigned long getSize(log_id_t id);
diff --git a/logd/LogBufferElement.cpp b/logd/LogBufferElement.cpp
index 81356fe..04a620c 100644
--- a/logd/LogBufferElement.cpp
+++ b/logd/LogBufferElement.cpp
@@ -235,7 +235,9 @@
}
iovec[1].iov_len = entry.len;
- log_time retval = reader->sendDatav(iovec, 2) ? FLUSH_ERROR : mRealTime;
+ log_time retval = reader->sendDatav(iovec, 1 + (entry.len != 0))
+ ? FLUSH_ERROR
+ : mRealTime;
if (buffer) free(buffer);
diff --git a/logd/LogKlog.cpp b/logd/LogKlog.cpp
index 9dfa14e..d23254d 100644
--- a/logd/LogKlog.cpp
+++ b/logd/LogKlog.cpp
@@ -37,46 +37,49 @@
static const char priority_message[] = { KMSG_PRIORITY(LOG_INFO), '\0' };
+// List of the _only_ needles we supply here to android::strnstr
+static const char suspendStr[] = "PM: suspend entry ";
+static const char resumeStr[] = "PM: suspend exit ";
+static const char suspendedStr[] = "Suspended for ";
+static const char healthdStr[] = "healthd";
+static const char batteryStr[] = ": battery ";
+static const char auditStr[] = " audit(";
+static const char klogdStr[] = "logd.klogd: ";
+
// Parsing is hard
// called if we see a '<', s is the next character, returns pointer after '>'
-static char* is_prio(char* s, size_t len) {
- if (!len || !isdigit(*s++)) {
- return NULL;
- }
+static char* is_prio(char* s, ssize_t len) {
+ if ((len <= 0) || !isdigit(*s++)) return nullptr;
--len;
static const size_t max_prio_len = (len < 4) ? len : 4;
size_t priolen = 0;
char c;
while (((c = *s++)) && (++priolen <= max_prio_len)) {
- if (!isdigit(c)) {
- return ((c == '>') && (*s == '[')) ? s : NULL;
- }
+ if (!isdigit(c)) return ((c == '>') && (*s == '[')) ? s : nullptr;
}
- return NULL;
+ return nullptr;
}
// called if we see a '[', s is the next character, returns pointer after ']'
-static char* is_timestamp(char* s, size_t len) {
- while (len && (*s == ' ')) {
+static char* is_timestamp(char* s, ssize_t len) {
+ while ((len > 0) && (*s == ' ')) {
++s;
--len;
}
- if (!len || !isdigit(*s++)) {
- return NULL;
- }
+ if ((len <= 0) || !isdigit(*s++)) return nullptr;
--len;
bool first_period = true;
char c;
- while (len && ((c = *s++))) {
+ while ((len > 0) && ((c = *s++))) {
--len;
if ((c == '.') && first_period) {
first_period = false;
} else if (!isdigit(c)) {
- return ((c == ']') && !first_period && (*s == ' ')) ? s : NULL;
+ return ((c == ']') && !first_period && (*s == ' ')) ? s : nullptr;
}
}
- return NULL;
+ return nullptr;
}
// Like strtok_r with "\r\n" except that we look for log signatures (regex)
@@ -93,96 +96,82 @@
// space is one more than <digit> of 9
#define OPEN_BRACKET_SPACE ((char)(OPEN_BRACKET_SIG | 10))
-char* log_strntok_r(char* s, size_t* len, char** last, size_t* sublen) {
- *sublen = 0;
- if (!*len) {
- return NULL;
- }
+char* android::log_strntok_r(char* s, ssize_t& len, char*& last,
+ ssize_t& sublen) {
+ sublen = 0;
+ if (len <= 0) return nullptr;
if (!s) {
- if (!(s = *last)) {
- return NULL;
- }
+ if (!(s = last)) return nullptr;
// fixup for log signature split <,
// LESS_THAN_SIG + <digit>
if ((*s & SIGNATURE_MASK) == LESS_THAN_SIG) {
*s = (*s & ~SIGNATURE_MASK) + '0';
*--s = '<';
- ++*len;
+ ++len;
}
// fixup for log signature split [,
// OPEN_BRACKET_SPACE is space, OPEN_BRACKET_SIG + <digit>
if ((*s & SIGNATURE_MASK) == OPEN_BRACKET_SIG) {
- if (*s == OPEN_BRACKET_SPACE) {
- *s = ' ';
- } else {
- *s = (*s & ~SIGNATURE_MASK) + '0';
- }
+ *s = (*s == OPEN_BRACKET_SPACE) ? ' ' : (*s & ~SIGNATURE_MASK) + '0';
*--s = '[';
- ++*len;
+ ++len;
}
}
- while (*len && ((*s == '\r') || (*s == '\n'))) {
+ while ((len > 0) && ((*s == '\r') || (*s == '\n'))) {
++s;
- --*len;
+ --len;
}
- if (!*len) {
- *last = NULL;
- return NULL;
- }
+ if (len <= 0) return last = nullptr;
char *peek, *tok = s;
for (;;) {
- if (*len == 0) {
- *last = NULL;
+ if (len <= 0) {
+ last = nullptr;
return tok;
}
char c = *s++;
- --*len;
- size_t adjust;
+ --len;
+ ssize_t adjust;
switch (c) {
case '\r':
case '\n':
s[-1] = '\0';
- *last = s;
+ last = s;
return tok;
case '<':
- peek = is_prio(s, *len);
- if (!peek) {
- break;
- }
+ peek = is_prio(s, len);
+ if (!peek) break;
if (s != (tok + 1)) { // not first?
s[-1] = '\0';
*s &= ~SIGNATURE_MASK;
*s |= LESS_THAN_SIG; // signature for '<'
- *last = s;
+ last = s;
return tok;
}
adjust = peek - s;
- if (adjust > *len) {
- adjust = *len;
+ if (adjust > len) {
+ adjust = len;
}
- *sublen += adjust;
- *len -= adjust;
+ sublen += adjust;
+ len -= adjust;
s = peek;
- if ((*s == '[') && ((peek = is_timestamp(s + 1, *len - 1)))) {
+ if ((*s == '[') && ((peek = is_timestamp(s + 1, len - 1)))) {
adjust = peek - s;
- if (adjust > *len) {
- adjust = *len;
+ if (adjust > len) {
+ adjust = len;
}
- *sublen += adjust;
- *len -= adjust;
+ sublen += adjust;
+ len -= adjust;
s = peek;
}
break;
case '[':
- peek = is_timestamp(s, *len);
- if (!peek) {
- break;
- }
+ peek = is_timestamp(s, len);
+ if (!peek) break;
if (s != (tok + 1)) { // not first?
s[-1] = '\0';
if (*s == ' ') {
@@ -191,19 +180,19 @@
*s &= ~SIGNATURE_MASK;
*s |= OPEN_BRACKET_SIG; // signature for '['
}
- *last = s;
+ last = s;
return tok;
}
adjust = peek - s;
- if (adjust > *len) {
- adjust = *len;
+ if (adjust > len) {
+ adjust = len;
}
- *sublen += adjust;
- *len -= adjust;
+ sublen += adjust;
+ len -= adjust;
s = peek;
break;
}
- ++*sublen;
+ ++sublen;
}
// NOTREACHED
}
@@ -222,9 +211,10 @@
initialized(false),
enableLogging(true),
auditd(auditd) {
- static const char klogd_message[] = "%slogd.klogd: %" PRIu64 "\n";
- char buffer[sizeof(priority_message) + sizeof(klogd_message) + 20 - 4];
- snprintf(buffer, sizeof(buffer), klogd_message, priority_message,
+ static const char klogd_message[] = "%s%s%" PRIu64 "\n";
+ char buffer[strlen(priority_message) + strlen(klogdStr) +
+ strlen(klogd_message) + 20];
+ snprintf(buffer, sizeof(buffer), klogd_message, priority_message, klogdStr,
signature.nsec());
write(fdWrite, buffer, strlen(buffer));
}
@@ -237,15 +227,15 @@
}
char buffer[LOGGER_ENTRY_MAX_PAYLOAD];
- size_t len = 0;
+ ssize_t len = 0;
for (;;) {
ssize_t retval = 0;
- if ((sizeof(buffer) - 1 - len) > 0) {
+ if (len < (ssize_t)(sizeof(buffer) - 1)) {
retval =
read(cli->getSocket(), buffer + len, sizeof(buffer) - 1 - len);
}
- if ((retval == 0) && (len == 0)) {
+ if ((retval == 0) && (len <= 0)) {
break;
}
if (retval < 0) {
@@ -255,15 +245,16 @@
bool full = len == (sizeof(buffer) - 1);
char* ep = buffer + len;
*ep = '\0';
- size_t sublen;
- for (char *ptr = NULL, *tok = buffer;
- ((tok = log_strntok_r(tok, &len, &ptr, &sublen))); tok = NULL) {
+ ssize_t sublen;
+ for (char *ptr = nullptr, *tok = buffer;
+ !!(tok = android::log_strntok_r(tok, len, ptr, sublen));
+ tok = nullptr) {
if (((tok + sublen) >= ep) && (retval != 0) && full) {
- memmove(buffer, tok, sublen);
+ if (sublen > 0) memmove(buffer, tok, sublen);
len = sublen;
break;
}
- if (*tok) {
+ if ((sublen > 0) && *tok) {
log(tok, sublen);
}
}
@@ -273,9 +264,12 @@
}
void LogKlog::calculateCorrection(const log_time& monotonic,
- const char* real_string, size_t len) {
+ const char* real_string, ssize_t len) {
+ static const char real_format[] = "%Y-%m-%d %H:%M:%S.%09q UTC";
+ if (len < (ssize_t)(strlen(real_format) + 5)) return;
+
log_time real;
- const char* ep = real.strptime(real_string, "%Y-%m-%d %H:%M:%S.%09q UTC");
+ const char* ep = real.strptime(real_string, real_format);
if (!ep || (ep > &real_string[len]) || (real > log_time(CLOCK_REALTIME))) {
return;
}
@@ -299,73 +293,50 @@
}
}
-static const char suspendStr[] = "PM: suspend entry ";
-static const char resumeStr[] = "PM: suspend exit ";
-static const char suspendedStr[] = "Suspended for ";
-
-const char* android::strnstr(const char* s, size_t len, const char* needle) {
- char c;
-
- if (!len) return NULL;
- if ((c = *needle++) != 0) {
- size_t needleLen = strlen(needle);
- do {
- do {
- if (len <= needleLen) return NULL;
- --len;
- } while (*s++ != c);
- } while (fastcmp<memcmp>(s, needle, needleLen));
- s--;
- }
- return s;
-}
-
-void LogKlog::sniffTime(log_time& now, const char** buf, size_t len,
+void LogKlog::sniffTime(log_time& now, const char*& buf, ssize_t len,
bool reverse) {
- const char* cp = now.strptime(*buf, "[ %s.%q]");
- if (cp && (cp >= &(*buf)[len])) {
- cp = NULL;
+ if (len <= 0) return;
+
+ const char* cp = nullptr;
+ if ((len > 10) && (*buf == '[')) {
+ cp = now.strptime(buf, "[ %s.%q]"); // can index beyond buffer bounds
+ if (cp && (cp > &buf[len - 1])) cp = nullptr;
}
if (cp) {
- static const char healthd[] = "healthd";
- static const char battery[] = ": battery ";
-
- len -= cp - *buf;
- if (len && isspace(*cp)) {
+ len -= cp - buf;
+ if ((len > 0) && isspace(*cp)) {
++cp;
--len;
}
- *buf = cp;
+ buf = cp;
- if (isMonotonic()) {
- return;
- }
+ if (isMonotonic()) return;
const char* b;
if (((b = android::strnstr(cp, len, suspendStr))) &&
- ((size_t)((b += sizeof(suspendStr) - 1) - cp) < len)) {
+ (((b += strlen(suspendStr)) - cp) < len)) {
len -= b - cp;
calculateCorrection(now, b, len);
} else if (((b = android::strnstr(cp, len, resumeStr))) &&
- ((size_t)((b += sizeof(resumeStr) - 1) - cp) < len)) {
+ (((b += strlen(resumeStr)) - cp) < len)) {
len -= b - cp;
calculateCorrection(now, b, len);
- } else if (((b = android::strnstr(cp, len, healthd))) &&
- ((size_t)((b += sizeof(healthd) - 1) - cp) < len) &&
- ((b = android::strnstr(b, len -= b - cp, battery))) &&
- ((size_t)((b += sizeof(battery) - 1) - cp) < len)) {
+ } else if (((b = android::strnstr(cp, len, healthdStr))) &&
+ (((b += strlen(healthdStr)) - cp) < len) &&
+ ((b = android::strnstr(b, len -= b - cp, batteryStr))) &&
+ (((b += strlen(batteryStr)) - cp) < len)) {
// NB: healthd is roughly 150us late, so we use it instead to
// trigger a check for ntp-induced or hardware clock drift.
log_time real(CLOCK_REALTIME);
log_time mono(CLOCK_MONOTONIC);
correction = (real < mono) ? log_time::EPOCH : (real - mono);
} else if (((b = android::strnstr(cp, len, suspendedStr))) &&
- ((size_t)((b += sizeof(suspendStr) - 1) - cp) < len)) {
+ (((b += strlen(suspendStr)) - cp) < len)) {
len -= b - cp;
log_time real;
char* endp;
real.tv_sec = strtol(b, &endp, 10);
- if ((*endp == '.') && ((size_t)(endp - b) < len)) {
+ if ((*endp == '.') && ((endp - b) < len)) {
unsigned long multiplier = NS_PER_SEC;
real.tv_nsec = 0;
len -= endp - b;
@@ -394,8 +365,15 @@
}
}
-pid_t LogKlog::sniffPid(const char** buf, size_t len) {
- const char* cp = *buf;
+pid_t LogKlog::sniffPid(const char*& buf, ssize_t len) {
+ if (len <= 0) return 0;
+
+ const char* cp = buf;
+ // sscanf does a strlen, let's check if the string is not nul terminated.
+ // pseudo out-of-bounds access since we always have an extra char on buffer.
+ if (((ssize_t)strnlen(cp, len) == len) && cp[len]) {
+ return 0;
+ }
// HTC kernels with modified printk "c0 1648 "
if ((len > 9) && (cp[0] == 'c') && isdigit(cp[1]) &&
(isdigit(cp[2]) || (cp[2] == ' ')) && (cp[3] == ' ')) {
@@ -412,7 +390,7 @@
int pid = 0;
char dummy;
if (sscanf(cp + 4, "%d%c", &pid, &dummy) == 2) {
- *buf = cp + 10; // skip-it-all
+ buf = cp + 10; // skip-it-all
return pid;
}
}
@@ -434,28 +412,28 @@
}
// kernel log prefix, convert to a kernel log priority number
-static int parseKernelPrio(const char** buf, size_t len) {
+static int parseKernelPrio(const char*& buf, ssize_t len) {
int pri = LOG_USER | LOG_INFO;
- const char* cp = *buf;
- if (len && (*cp == '<')) {
+ const char* cp = buf;
+ if ((len > 0) && (*cp == '<')) {
pri = 0;
while (--len && isdigit(*++cp)) {
pri = (pri * 10) + *cp - '0';
}
- if (len && (*cp == '>')) {
+ if ((len > 0) && (*cp == '>')) {
++cp;
} else {
- cp = *buf;
+ cp = buf;
pri = LOG_USER | LOG_INFO;
}
- *buf = cp;
+ buf = cp;
}
return pri;
}
// Passed the entire SYSLOG_ACTION_READ_ALL buffer and interpret a
// compensated start time.
-void LogKlog::synchronize(const char* buf, size_t len) {
+void LogKlog::synchronize(const char* buf, ssize_t len) {
const char* cp = android::strnstr(buf, len, suspendStr);
if (!cp) {
cp = android::strnstr(buf, len, resumeStr);
@@ -471,10 +449,10 @@
if (*cp == '\n') {
++cp;
}
- parseKernelPrio(&cp, len - (cp - buf));
+ parseKernelPrio(cp, len - (cp - buf));
log_time now;
- sniffTime(now, &cp, len - (cp - buf), true);
+ sniffTime(now, cp, len - (cp - buf), true);
const char* suspended = android::strnstr(buf, len, suspendedStr);
if (!suspended || (suspended > cp)) {
@@ -488,9 +466,9 @@
if (*cp == '\n') {
++cp;
}
- parseKernelPrio(&cp, len - (cp - buf));
+ parseKernelPrio(cp, len - (cp - buf));
- sniffTime(now, &cp, len - (cp - buf), true);
+ sniffTime(now, cp, len - (cp - buf), true);
}
// Convert kernel log priority number into an Android Logger priority number
@@ -523,9 +501,9 @@
return ANDROID_LOG_INFO;
}
-static const char* strnrchr(const char* s, size_t len, char c) {
- const char* save = NULL;
- for (; len; ++s, len--) {
+static const char* strnrchr(const char* s, ssize_t len, char c) {
+ const char* save = nullptr;
+ for (; len > 0; ++s, len--) {
if (*s == c) {
save = s;
}
@@ -566,22 +544,21 @@
// logd.klogd:
// return -1 if message logd.klogd: <signature>
//
-int LogKlog::log(const char* buf, size_t len) {
- if (auditd && android::strnstr(buf, len, " audit(")) {
+int LogKlog::log(const char* buf, ssize_t len) {
+ if (auditd && android::strnstr(buf, len, auditStr)) {
return 0;
}
const char* p = buf;
- int pri = parseKernelPrio(&p, len);
+ int pri = parseKernelPrio(p, len);
log_time now;
- sniffTime(now, &p, len - (p - buf), false);
+ sniffTime(now, p, len - (p - buf), false);
// sniff for start marker
- const char klogd_message[] = "logd.klogd: ";
- const char* start = android::strnstr(p, len - (p - buf), klogd_message);
+ const char* start = android::strnstr(p, len - (p - buf), klogdStr);
if (start) {
- uint64_t sig = strtoll(start + sizeof(klogd_message) - 1, NULL, 10);
+ uint64_t sig = strtoll(start + strlen(klogdStr), nullptr, 10);
if (sig == signature.nsec()) {
if (initialized) {
enableLogging = true;
@@ -598,7 +575,7 @@
}
// Parse pid, tid and uid
- const pid_t pid = sniffPid(&p, len - (p - buf));
+ const pid_t pid = sniffPid(p, len - (p - buf));
const pid_t tid = pid;
uid_t uid = AID_ROOT;
if (pid) {
@@ -620,11 +597,11 @@
start = p;
const char* tag = "";
const char* etag = tag;
- size_t taglen = len - (p - buf);
+ ssize_t taglen = len - (p - buf);
const char* bt = p;
static const char infoBrace[] = "[INFO]";
- static const size_t infoBraceLen = strlen(infoBrace);
+ static const ssize_t infoBraceLen = strlen(infoBrace);
if ((taglen >= infoBraceLen) &&
!fastcmp<strncmp>(p, infoBrace, infoBraceLen)) {
// <PRI>[<TIME>] "[INFO]"<tag> ":" message
@@ -633,26 +610,26 @@
}
const char* et;
- for (et = bt; taglen && *et && (*et != ':') && !isspace(*et);
+ for (et = bt; (taglen > 0) && *et && (*et != ':') && !isspace(*et);
++et, --taglen) {
// skip ':' within [ ... ]
if (*et == '[') {
- while (taglen && *et && *et != ']') {
+ while ((taglen > 0) && *et && *et != ']') {
++et;
--taglen;
}
- if (!taglen) {
+ if (taglen <= 0) {
break;
}
}
}
const char* cp;
- for (cp = et; taglen && isspace(*cp); ++cp, --taglen) {
+ for (cp = et; (taglen > 0) && isspace(*cp); ++cp, --taglen) {
}
// Validate tag
- size_t size = et - bt;
- if (taglen && size) {
+ ssize_t size = et - bt;
+ if ((taglen > 0) && (size > 0)) {
if (*cp == ':') {
// ToDo: handle case insensitive colon separated logging stutter:
// <tag> : <tag>: ...
@@ -672,12 +649,12 @@
const char* b = cp;
cp += size;
taglen -= size;
- while (--taglen && !isspace(*++cp) && (*cp != ':')) {
+ while ((--taglen > 0) && !isspace(*++cp) && (*cp != ':')) {
}
const char* e;
- for (e = cp; taglen && isspace(*cp); ++cp, --taglen) {
+ for (e = cp; (taglen > 0) && isspace(*cp); ++cp, --taglen) {
}
- if (taglen && (*cp == ':')) {
+ if ((taglen > 0) && (*cp == ':')) {
tag = b;
etag = e;
p = cp + 1;
@@ -685,7 +662,7 @@
} else {
// what about <PRI>[<TIME>] <tag>_host '<tag><stuff>' : message
static const char host[] = "_host";
- static const size_t hostlen = strlen(host);
+ static const ssize_t hostlen = strlen(host);
if ((size > hostlen) &&
!fastcmp<strncmp>(bt + size - hostlen, host, hostlen) &&
!fastcmp<strncmp>(bt + 1, cp + 1, size - hostlen - 1)) {
@@ -693,12 +670,14 @@
cp += size - hostlen;
taglen -= size - hostlen;
if (*cp == '.') {
- while (--taglen && !isspace(*++cp) && (*cp != ':')) {
+ while ((--taglen > 0) && !isspace(*++cp) &&
+ (*cp != ':')) {
}
const char* e;
- for (e = cp; taglen && isspace(*cp); ++cp, --taglen) {
+ for (e = cp; (taglen > 0) && isspace(*cp);
+ ++cp, --taglen) {
}
- if (taglen && (*cp == ':')) {
+ if ((taglen > 0) && (*cp == ':')) {
tag = b;
etag = e;
p = cp + 1;
@@ -711,13 +690,13 @@
} else {
// <PRI>[<TIME>] <tag> <stuff>' : message
twoWord:
- while (--taglen && !isspace(*++cp) && (*cp != ':')) {
+ while ((--taglen > 0) && !isspace(*++cp) && (*cp != ':')) {
}
const char* e;
- for (e = cp; taglen && isspace(*cp); ++cp, --taglen) {
+ for (e = cp; (taglen > 0) && isspace(*cp); ++cp, --taglen) {
}
// Two words
- if (taglen && (*cp == ':')) {
+ if ((taglen > 0) && (*cp == ':')) {
tag = bt;
etag = e;
p = cp + 1;
@@ -726,13 +705,13 @@
} // else no tag
static const char cpu[] = "CPU";
- static const size_t cpuLen = strlen(cpu);
+ static const ssize_t cpuLen = strlen(cpu);
static const char warning[] = "WARNING";
- static const size_t warningLen = strlen(warning);
+ static const ssize_t warningLen = strlen(warning);
static const char error[] = "ERROR";
- static const size_t errorLen = strlen(error);
+ static const ssize_t errorLen = strlen(error);
static const char info[] = "INFO";
- static const size_t infoLen = strlen(info);
+ static const ssize_t infoLen = strlen(info);
size = etag - tag;
if ((size <= 1) ||
@@ -756,13 +735,13 @@
// Mediatek-special printk induced stutter
const char* mp = strnrchr(tag, taglen, ']');
if (mp && (++mp < etag)) {
- size_t s = etag - mp;
+ ssize_t s = etag - mp;
if (((s + s) < taglen) && !fastcmp<memcmp>(mp, mp - 1 - s, s)) {
taglen = mp - tag;
}
}
// Deal with sloppy and simplistic harmless p = cp + 1 etc above.
- if (len < (size_t)(p - buf)) {
+ if (len < (p - buf)) {
p = &buf[len];
}
// skip leading space
@@ -770,12 +749,12 @@
++p;
}
// truncate trailing space or nuls
- size_t b = len - (p - buf);
- while (b && (isspace(p[b - 1]) || !p[b - 1])) {
+ ssize_t b = len - (p - buf);
+ while ((b > 0) && (isspace(p[b - 1]) || !p[b - 1])) {
--b;
}
// trick ... allow tag with empty content to be logged. log() drops empty
- if (!b && taglen) {
+ if ((b <= 0) && (taglen > 0)) {
p = " ";
b = 1;
}
@@ -787,9 +766,9 @@
taglen = LOGGER_ENTRY_MAX_PAYLOAD;
}
// calculate buffer copy requirements
- size_t n = 1 + taglen + 1 + b + 1;
+ ssize_t n = 1 + taglen + 1 + b + 1;
// paranoid sanity check, first two just can not happen ...
- if ((taglen > n) || (b > n) || (n > USHRT_MAX)) {
+ if ((taglen > n) || (b > n) || (n > (ssize_t)USHRT_MAX) || (n <= 0)) {
return -EINVAL;
}
diff --git a/logd/LogKlog.h b/logd/LogKlog.h
index 976afc8..bb92dd2 100644
--- a/logd/LogKlog.h
+++ b/logd/LogKlog.h
@@ -20,8 +20,6 @@
#include <private/android_logger.h>
#include <sysutils/SocketListener.h>
-char* log_strntok_r(char* s, size_t* len, char** saveptr, size_t* sublen);
-
class LogBuffer;
class LogReader;
@@ -43,8 +41,8 @@
public:
LogKlog(LogBuffer* buf, LogReader* reader, int fdWrite, int fdRead,
bool auditd);
- int log(const char* buf, size_t len);
- void synchronize(const char* buf, size_t len);
+ int log(const char* buf, ssize_t len);
+ void synchronize(const char* buf, ssize_t len);
bool isMonotonic() {
return logbuf->isMonotonic();
@@ -57,10 +55,10 @@
}
protected:
- void sniffTime(log_time& now, const char** buf, size_t len, bool reverse);
- pid_t sniffPid(const char** buf, size_t len);
+ void sniffTime(log_time& now, const char*& buf, ssize_t len, bool reverse);
+ pid_t sniffPid(const char*& buf, ssize_t len);
void calculateCorrection(const log_time& monotonic, const char* real_string,
- size_t len);
+ ssize_t len);
virtual bool onDataAvailable(SocketClient* cli);
};
diff --git a/logd/LogReader.cpp b/logd/LogReader.cpp
index 620d4d0..af19279 100644
--- a/logd/LogReader.cpp
+++ b/logd/LogReader.cpp
@@ -182,7 +182,7 @@
} logFindStart(pid, logMask, sequence,
logbuf().isMonotonic() && android::isMonotonic(start));
- logbuf().flushTo(cli, sequence, FlushCommand::hasReadLogs(cli),
+ logbuf().flushTo(cli, sequence, nullptr, FlushCommand::hasReadLogs(cli),
FlushCommand::hasSecurityLogs(cli),
logFindStart.callback, &logFindStart);
diff --git a/logd/LogTimes.cpp b/logd/LogTimes.cpp
index 04e531f..ccc550a 100644
--- a/logd/LogTimes.cpp
+++ b/logd/LogTimes.cpp
@@ -15,6 +15,7 @@
*/
#include <errno.h>
+#include <string.h>
#include <sys/prctl.h>
#include <private/android_logger.h>
@@ -47,7 +48,8 @@
mEnd(log_time(android_log_clockid())) {
mTimeout.tv_sec = timeout / NS_PER_SEC;
mTimeout.tv_nsec = timeout % NS_PER_SEC;
- pthread_cond_init(&threadTriggeredCondition, NULL);
+ memset(mLastTid, 0, sizeof(mLastTid));
+ pthread_cond_init(&threadTriggeredCondition, nullptr);
cleanSkip_Locked();
}
@@ -98,7 +100,7 @@
it++;
}
- me->mClient = NULL;
+ me->mClient = nullptr;
reader.release(client);
}
@@ -122,7 +124,7 @@
SocketClient* client = me->mClient;
if (!client) {
me->error();
- return NULL;
+ return nullptr;
}
LogBuffer& logbuf = me->mReader.logbuf();
@@ -151,12 +153,12 @@
unlock();
if (me->mTail) {
- logbuf.flushTo(client, start, privileged, security, FilterFirstPass,
- me);
+ logbuf.flushTo(client, start, nullptr, privileged, security,
+ FilterFirstPass, me);
me->leadingDropped = true;
}
- start = logbuf.flushTo(client, start, privileged, security,
- FilterSecondPass, me);
+ start = logbuf.flushTo(client, start, me->mLastTid, privileged,
+ security, FilterSecondPass, me);
lock();
@@ -182,7 +184,7 @@
pthread_cleanup_pop(true);
- return NULL;
+ return nullptr;
}
// A first pass to count the number of elements
@@ -281,7 +283,5 @@
}
void LogTimeEntry::cleanSkip_Locked(void) {
- for (log_id_t i = LOG_ID_MIN; i < LOG_ID_MAX; i = (log_id_t)(i + 1)) {
- skipAhead[i] = 0;
- }
+ memset(skipAhead, 0, sizeof(skipAhead));
}
diff --git a/logd/LogTimes.h b/logd/LogTimes.h
index 9a3ddab..ec8252e 100644
--- a/logd/LogTimes.h
+++ b/logd/LogTimes.h
@@ -44,6 +44,7 @@
const unsigned int mLogMask;
const pid_t mPid;
unsigned int skipAhead[LOG_ID_MAX];
+ pid_t mLastTid[LOG_ID_MAX];
unsigned long mCount;
unsigned long mTail;
unsigned long mIndex;
diff --git a/logd/LogUtils.h b/logd/LogUtils.h
index 93d2a79..fa9f398 100644
--- a/logd/LogUtils.h
+++ b/logd/LogUtils.h
@@ -43,8 +43,25 @@
const char* tagToName(uint32_t tag);
void ReReadEventLogTags();
-// Furnished by LogKlog.cpp.
-const char* strnstr(const char* s, size_t len, const char* needle);
+// Furnished by LogKlog.cpp
+char* log_strntok_r(char* s, ssize_t& len, char*& saveptr, ssize_t& sublen);
+
+// needle should reference a string longer than 1 character
+static inline const char* strnstr(const char* s, ssize_t len,
+ const char* needle) {
+ if (len <= 0) return nullptr;
+
+ const char c = *needle++;
+ const size_t needleLen = strlen(needle);
+ do {
+ do {
+ if (len <= (ssize_t)needleLen) return nullptr;
+ --len;
+ } while (*s++ != c);
+ } while (fastcmp<memcmp>(s, needle, needleLen));
+ s--;
+ return s;
+}
}
// Furnished in LogCommand.cpp
diff --git a/logd/main.cpp b/logd/main.cpp
index 3334506..946485b 100644
--- a/logd/main.cpp
+++ b/logd/main.cpp
@@ -221,7 +221,7 @@
static sem_t reinit;
static bool reinit_running = false;
-static LogBuffer* logBuf = NULL;
+static LogBuffer* logBuf = nullptr;
static bool package_list_parser_cb(pkg_info* info, void* /* userdata */) {
bool rc = true;
@@ -254,9 +254,9 @@
while (reinit_running && !sem_wait(&reinit) && reinit_running) {
// uidToName Privileged Worker
if (uid) {
- name = NULL;
+ name = nullptr;
- packagelist_parse(package_list_parser_cb, NULL);
+ packagelist_parse(package_list_parser_cb, nullptr);
uid = 0;
sem_post(&uidName);
@@ -291,19 +291,19 @@
// Anything that reads persist.<property>
if (logBuf) {
logBuf->init();
- logBuf->initPrune(NULL);
+ logBuf->initPrune(nullptr);
}
android::ReReadEventLogTags();
}
- return NULL;
+ return nullptr;
}
static sem_t sem_name;
char* android::uidToName(uid_t u) {
if (!u || !reinit_running) {
- return NULL;
+ return nullptr;
}
sem_wait(&sem_name);
@@ -311,7 +311,7 @@
// Not multi-thread safe, we use sem_name to protect
uid = u;
- name = NULL;
+ name = nullptr;
sem_post(&reinit);
sem_wait(&uidName);
char* ret = name;
@@ -332,12 +332,13 @@
return;
}
- int rc = klogctl(KLOG_SIZE_BUFFER, NULL, 0);
+ int rc = klogctl(KLOG_SIZE_BUFFER, nullptr, 0);
if (rc <= 0) {
return;
}
- size_t len = rc + 1024; // Margin for additional input race or trailing nul
+ // Margin for additional input race or trailing nul
+ ssize_t len = rc + 1024;
std::unique_ptr<char[]> buf(new char[len]);
rc = klogctl(KLOG_READ_ALL, buf.get(), len);
@@ -345,7 +346,7 @@
return;
}
- if ((size_t)rc < len) {
+ if (rc < len) {
len = rc + 1;
}
buf[--len] = '\0';
@@ -354,10 +355,11 @@
kl->synchronize(buf.get(), len);
}
- size_t sublen;
- for (char *ptr = NULL, *tok = buf.get();
- (rc >= 0) && ((tok = log_strntok_r(tok, &len, &ptr, &sublen)));
- tok = NULL) {
+ ssize_t sublen;
+ for (char *ptr = nullptr, *tok = buf.get();
+ (rc >= 0) && !!(tok = android::log_strntok_r(tok, len, ptr, sublen));
+ tok = nullptr) {
+ if ((sublen <= 0) || !*tok) continue;
if (al) {
rc = al->log(tok, sublen);
}
@@ -444,7 +446,7 @@
if (!pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)) {
pthread_t thread;
reinit_running = true;
- if (pthread_create(&thread, &attr, reinit_thread_start, NULL)) {
+ if (pthread_create(&thread, &attr, reinit_thread_start, nullptr)) {
reinit_running = false;
}
}
@@ -507,7 +509,7 @@
// initiated log messages. New log entries are added to LogBuffer
// and LogReader is notified to send updates to connected clients.
- LogAudit* al = NULL;
+ LogAudit* al = nullptr;
if (auditd) {
al = new LogAudit(logBuf, reader,
__android_logger_property_get_bool(
@@ -516,9 +518,9 @@
: -1);
}
- LogKlog* kl = NULL;
+ LogKlog* kl = nullptr;
if (klogd) {
- kl = new LogKlog(logBuf, reader, fdDmesg, fdPmesg, al != NULL);
+ kl = new LogKlog(logBuf, reader, fdDmesg, fdPmesg, al != nullptr);
}
readDmesg(al, kl);
diff --git a/logd/tests/logd_test.cpp b/logd/tests/logd_test.cpp
index ddff393..88cb67a 100644
--- a/logd/tests/logd_test.cpp
+++ b/logd/tests/logd_test.cpp
@@ -426,7 +426,7 @@
" BM_log_maximum_retry"
" BM_log_maximum"
" BM_clock_overhead"
- " BM_log_overhead"
+ " BM_log_print_overhead"
" BM_log_latency"
" BM_log_delay",
"r")));
@@ -434,13 +434,13 @@
char buffer[5120];
static const char* benchmarks[] = {
- "BM_log_maximum_retry ", "BM_log_maximum ", "BM_clock_overhead ",
- "BM_log_overhead ", "BM_log_latency ", "BM_log_delay "
+ "BM_log_maximum_retry ", "BM_log_maximum ", "BM_clock_overhead ",
+ "BM_log_print_overhead ", "BM_log_latency ", "BM_log_delay "
};
static const unsigned int log_maximum_retry = 0;
static const unsigned int log_maximum = 1;
static const unsigned int clock_overhead = 2;
- static const unsigned int log_overhead = 3;
+ static const unsigned int log_print_overhead = 3;
static const unsigned int log_latency = 4;
static const unsigned int log_delay = 5;
@@ -469,21 +469,23 @@
}
EXPECT_GE(200000UL, ns[log_maximum_retry]); // 104734 user
+ EXPECT_NE(0UL, ns[log_maximum_retry]); // failure to parse
EXPECT_GE(90000UL, ns[log_maximum]); // 46913 user
+ EXPECT_NE(0UL, ns[log_maximum]); // failure to parse
EXPECT_GE(4096UL, ns[clock_overhead]); // 4095
+ EXPECT_NE(0UL, ns[clock_overhead]); // failure to parse
- EXPECT_GE(250000UL, ns[log_overhead]); // 126886 user
+ EXPECT_GE(250000UL, ns[log_print_overhead]); // 126886 user
+ EXPECT_NE(0UL, ns[log_print_overhead]); // failure to parse
EXPECT_GE(10000000UL,
ns[log_latency]); // 1453559 user space (background cgroup)
+ EXPECT_NE(0UL, ns[log_latency]); // failure to parse
EXPECT_GE(20000000UL, ns[log_delay]); // 10500289 user
-
- for (unsigned i = 0; i < arraysize(ns); ++i) {
- EXPECT_NE(0UL, ns[i]);
- }
+ EXPECT_NE(0UL, ns[log_delay]); // failure to parse
alloc_statistics(&buf, &len);
@@ -997,16 +999,18 @@
}
// We may have DAC, but let's not have MAC
- if (setcon("u:object_r:shell:s0") < 0) {
+ if ((setcon("u:object_r:shell:s0") < 0) && (setcon("u:r:shell:s0") < 0)) {
int save_errno = errno;
security_context_t context;
getcon(&context);
- fprintf(stderr, "setcon(\"u:r:shell:s0\") failed @\"%s\" %s\n", context,
- strerror(save_errno));
- freecon(context);
- _exit(-1);
- // NOTREACHED
- return 0;
+ if (strcmp(context, "u:r:shell:s0")) {
+ fprintf(stderr, "setcon(\"u:r:shell:s0\") failed @\"%s\" %s\n",
+ context, strerror(save_errno));
+ freecon(context);
+ _exit(-1);
+ // NOTREACHED
+ return 0;
+ }
}
// The key here is we are root, but we are in u:r:shell:s0,
diff --git a/qemu_pipe/Android.mk b/qemu_pipe/Android.mk
deleted file mode 100644
index 6e0144c..0000000
--- a/qemu_pipe/Android.mk
+++ /dev/null
@@ -1,19 +0,0 @@
-# Copyright 2011 The Android Open Source Project
-
-LOCAL_PATH:= $(call my-dir)
-
-common_static_libraries := \
- libbase
-include $(CLEAR_VARS)
-LOCAL_CLANG := true
-LOCAL_SANITIZE := integer
-LOCAL_SRC_FILES:= \
- qemu_pipe.cpp
-LOCAL_C_INCLUDES := \
- $(LOCAL_PATH)/include \
- system/base/include
-LOCAL_MODULE:= libqemu_pipe
-LOCAL_STATIC_LIBRARIES := $(common_static_libraries)
-LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_CFLAGS := -Werror
-include $(BUILD_STATIC_LIBRARY)
diff --git a/qemu_pipe/include/qemu_pipe.h b/qemu_pipe/include/qemu_pipe.h
deleted file mode 100644
index 0987498..0000000
--- a/qemu_pipe/include/qemu_pipe.h
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2011 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 ANDROID_CORE_INCLUDE_QEMU_PIPE_H
-#define ANDROID_CORE_INCLUDE_QEMU_PIPE_H
-
-#include <stddef.h>
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-// Try to open a new Qemu fast-pipe. This function returns a file descriptor
-// that can be used to communicate with a named service managed by the
-// emulator.
-//
-// This file descriptor can be used as a standard pipe/socket descriptor.
-//
-// 'pipeName' is the name of the emulator service you want to connect to,
-// and should begin with 'pipe:' (e.g. 'pipe:camera' or 'pipe:opengles').
-// For backward compatibility, the 'pipe:' prefix can be omitted, and in
-// that case, qemu_pipe_open will add it for you.
-
-// On success, return a valid file descriptor, or -1/errno on failure. E.g.:
-//
-// EINVAL -> unknown/unsupported pipeName
-// ENOSYS -> fast pipes not available in this system.
-//
-// ENOSYS should never happen, except if you're trying to run within a
-// misconfigured emulator.
-//
-// You should be able to open several pipes to the same pipe service,
-// except for a few special cases (e.g. GSM modem), where EBUSY will be
-// returned if more than one client tries to connect to it.
-int qemu_pipe_open(const char* pipeName);
-
-// Send a framed message |buff| of |len| bytes through the |fd| descriptor.
-// This really adds a 4-hexchar prefix describing the payload size.
-// Returns 0 on success, and -1 on error.
-int qemu_pipe_frame_send(int fd, const void* buff, size_t len);
-
-// Read a frame message from |fd|, and store it into |buff| of |len| bytes.
-// If the framed message is larger than |len|, then this returns -1 and the
-// content is lost. Otherwise, this returns the size of the message. NOTE:
-// empty messages are possible in a framed wire protocol and do not mean
-// end-of-stream.
-int qemu_pipe_frame_recv(int fd, void* buff, size_t len);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* ANDROID_CORE_INCLUDE_QEMU_PIPE_H */
diff --git a/qemu_pipe/qemu_pipe.cpp b/qemu_pipe/qemu_pipe.cpp
deleted file mode 100644
index beeccb0..0000000
--- a/qemu_pipe/qemu_pipe.cpp
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * Copyright (C) 2011 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 "qemu_pipe.h"
-
-#include <unistd.h>
-#include <fcntl.h>
-#include <string.h>
-#include <errno.h>
-#include <stdio.h>
-
-#include <android-base/file.h>
-
-using android::base::ReadFully;
-using android::base::WriteFully;
-
-// Define QEMU_PIPE_DEBUG if you want to print error messages when an error
-// occurs during pipe operations. The macro should simply take a printf-style
-// formatting string followed by optional arguments.
-#ifndef QEMU_PIPE_DEBUG
-# define QEMU_PIPE_DEBUG(...) (void)0
-#endif
-
-int qemu_pipe_open(const char* pipeName) {
- // Sanity check.
- if (!pipeName) {
- errno = EINVAL;
- return -1;
- }
-
- int fd = TEMP_FAILURE_RETRY(open("/dev/qemu_pipe", O_RDWR));
- if (fd < 0) {
- QEMU_PIPE_DEBUG("%s: Could not open /dev/qemu_pipe: %s", __FUNCTION__,
- strerror(errno));
- return -1;
- }
-
- // Write the pipe name, *including* the trailing zero which is necessary.
- size_t pipeNameLen = strlen(pipeName);
- if (WriteFully(fd, pipeName, pipeNameLen + 1U)) {
- return fd;
- }
-
- // now, add 'pipe:' prefix and try again
- // Note: host side will wait for the trailing '\0' to start
- // service lookup.
- const char pipe_prefix[] = "pipe:";
- if (WriteFully(fd, pipe_prefix, strlen(pipe_prefix)) &&
- WriteFully(fd, pipeName, pipeNameLen + 1U)) {
- return fd;
- }
- QEMU_PIPE_DEBUG("%s: Could not write to %s pipe service: %s",
- __FUNCTION__, pipeName, strerror(errno));
- close(fd);
- return -1;
-}
-
-int qemu_pipe_frame_send(int fd, const void* buff, size_t len) {
- char header[5];
- snprintf(header, sizeof(header), "%04zx", len);
- if (!WriteFully(fd, header, 4)) {
- QEMU_PIPE_DEBUG("Can't write qemud frame header: %s", strerror(errno));
- return -1;
- }
- if (!WriteFully(fd, buff, len)) {
- QEMU_PIPE_DEBUG("Can't write qemud frame payload: %s", strerror(errno));
- return -1;
- }
- return 0;
-}
-
-int qemu_pipe_frame_recv(int fd, void* buff, size_t len) {
- char header[5];
- if (!ReadFully(fd, header, 4)) {
- QEMU_PIPE_DEBUG("Can't read qemud frame header: %s", strerror(errno));
- return -1;
- }
- header[4] = '\0';
- size_t size;
- if (sscanf(header, "%04zx", &size) != 1) {
- QEMU_PIPE_DEBUG("Malformed qemud frame header: [%.*s]", 4, header);
- return -1;
- }
- if (size > len) {
- QEMU_PIPE_DEBUG("Oversized qemud frame (% bytes, expected <= %)", size,
- len);
- return -1;
- }
- if (!ReadFully(fd, buff, size)) {
- QEMU_PIPE_DEBUG("Could not read qemud frame payload: %s",
- strerror(errno));
- return -1;
- }
- return size;
-}
diff --git a/rootdir/etc/ld.config.txt b/rootdir/etc/ld.config.txt
new file mode 100644
index 0000000..e3468ca
--- /dev/null
+++ b/rootdir/etc/ld.config.txt
@@ -0,0 +1,47 @@
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Bionic loader config file.
+#
+
+#dir.vendor=/vendor/bin/
+#dir.system=/system/bin/
+
+[vendor]
+
+# When this flag is set to true linker will
+# set target_sdk_version for this binary to
+# the version specified in <dirname>/.version
+# file, where <dirname> = dirname(executable_path)
+#
+# default value is false
+enable.target.sdk.version = true
+
+# There is always the default namespace no
+# need to mention it in this list
+additional.namespaces=system
+
+# Is the namespace isolated
+namespace.default.isolated = true
+namespace.default.search.paths = /vendor/${LIB}
+
+# TODO: property for asan search path?
+namespace.default.permitted.paths = /vendor/${LIB}
+namespace.default.asan.permitted.paths = /data/vendor/${LIB}
+namespace.default.links = system
+
+# Todo complete this list
+namespace.default.link.system.shared_libs = libc.so:libm.so:libdl.so:libstdc++.so
+
+namespace.system.isolated = true
+namespace.system.search.paths = /system/${LIB}:/system/${LIB}/framework
+namespace.system.permitted.paths = /system/${LIB}
+
+[system]
+namespace.default.isolated = true
+namespace.default.search.paths = /system/${LIB}
+namespace.default.permitted.paths = /system/${LIB}
+
+# app_process will setup additional vendor namespace manually.
+# Note that zygote will need vendor namespace to setup list of public
+# libraries provided by vendors to apps.
+
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 06dc88b..ff96c14 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -299,6 +299,11 @@
on post-fs
start logd
+ start hwservicemanager
+
+ # HALs required before data is mounted
+ class_start early_hal
+
# once everything is setup, no need to modify /
mount rootfs rootfs / ro remount
# Mount shared so changes propagate into child namespaces
@@ -346,9 +351,6 @@
# create the lost+found directories, so as to enforce our permissions
mkdir /cache/lost+found 0770 root root
-on late-fs
- start hwservicemanager
-
on post-fs-data
# We chown/chmod /data again so because mount is run as root + defaults
chown system system /data
@@ -411,6 +413,9 @@
mkdir /data/misc/profman 0770 system shell
mkdir /data/misc/gcov 0770 root root
+ mkdir /data/vendor 0771 root root
+ mkdir /data/vendor/hardware 0771 root root
+
# For security reasons, /data/local/tmp should always be empty.
# Do not place files or directories in /data/local/tmp
mkdir /data/local/tmp 0771 shell shell
@@ -596,7 +601,7 @@
on nonencrypted
# A/B update verifier that marks a successful boot.
- exec - root cache -- /system/bin/update_verifier nonencrypted
+ exec_start update_verifier_nonencrypted
class_start main
class_start late_start
@@ -619,12 +624,12 @@
on property:vold.decrypt=trigger_restart_min_framework
# A/B update verifier that marks a successful boot.
- exec - root cache -- /system/bin/update_verifier trigger_restart_min_framework
+ exec_start update_verifier
class_start main
on property:vold.decrypt=trigger_restart_framework
# A/B update verifier that marks a successful boot.
- exec - root cache -- /system/bin/update_verifier trigger_restart_framework
+ exec_start update_verifier
class_start main
class_start late_start
diff --git a/shell_and_utilities/Android.bp b/shell_and_utilities/Android.bp
new file mode 100644
index 0000000..81cf315
--- /dev/null
+++ b/shell_and_utilities/Android.bp
@@ -0,0 +1,13 @@
+phony {
+ name: "shell_and_utilities",
+ required: [
+ "bzip2",
+ "grep",
+ "gzip",
+ "mkshrc",
+ "reboot",
+ "sh",
+ "toolbox",
+ "toybox",
+ ],
+}
diff --git a/tzdatacheck/tzdatacheck.cpp b/tzdatacheck/tzdatacheck.cpp
index 381df5a..8fcd17f 100644
--- a/tzdatacheck/tzdatacheck.cpp
+++ b/tzdatacheck/tzdatacheck.cpp
@@ -30,6 +30,19 @@
#include "android-base/logging.h"
+// The name of the directory that holds a staged time zone update distro. If this exists it should
+// replace the one in CURRENT_DIR_NAME.
+// See also libcore.tzdata.update2.TimeZoneDistroInstaller.
+static const char* STAGED_DIR_NAME = "/staged";
+
+// The name of the directory that holds the (optional) installed time zone update distro.
+// See also libcore.tzdata.update2.TimeZoneDistroInstaller.
+static const char* CURRENT_DIR_NAME = "/current";
+
+// The name of a file in the staged dir that indicates the staged operation is an "uninstall".
+// See also libcore.tzdata.update2.TimeZoneDistroInstaller.
+static const char* UNINSTALL_TOMBSTONE_FILE_NAME = "/STAGED_UNINSTALL_TOMBSTONE";
+
// The name of the file containing the distro version information.
// See also libcore.tzdata.shared2.TimeZoneDistro / libcore.tzdata.shared2.DistroVersion.
static const char* DISTRO_VERSION_FILENAME = "/distro_version";
@@ -75,7 +88,6 @@
static const char TZ_DATA_HEADER_PREFIX[] = "tzdata";
static const size_t TZ_DATA_HEADER_PREFIX_LEN = sizeof(TZ_DATA_HEADER_PREFIX) - 1; // exclude \0
-
static void usage() {
std::cerr << "Usage: tzdatacheck SYSTEM_TZ_DIR DATA_TZ_DIR\n"
"\n"
@@ -184,7 +196,7 @@
return 0;
}
-enum PathStatus { ERR, NONE, IS_DIR, NOT_DIR };
+enum PathStatus { ERR, NONE, IS_DIR, IS_REG, UNKNOWN };
static PathStatus checkPath(const std::string& path) {
struct stat buf;
@@ -195,7 +207,31 @@
}
return NONE;
}
- return S_ISDIR(buf.st_mode) ? IS_DIR : NOT_DIR;
+ return S_ISDIR(buf.st_mode) ? IS_DIR : S_ISREG(buf.st_mode) ? IS_REG : UNKNOWN;
+}
+
+/*
+ * Deletes fileToDelete and returns true if it is successful. If fileToDelete is not a file or
+ * cannot be accessed this method returns false.
+ */
+static bool deleteFile(const std::string& fileToDelete) {
+ // Check whether the file exists.
+ PathStatus pathStatus = checkPath(fileToDelete);
+ if (pathStatus == NONE) {
+ LOG(INFO) << "Path " << fileToDelete << " does not exist";
+ return true;
+ }
+ if (pathStatus != IS_REG) {
+ LOG(WARNING) << "Path " << fileToDelete << " failed to stat() or is not a file.";
+ return false;
+ }
+
+ // Attempt the deletion.
+ int rc = unlink(fileToDelete.c_str());
+ if (rc != 0) {
+ PLOG(WARNING) << "unlink() failed for " << fileToDelete;
+ }
+ return rc == 0;
}
/*
@@ -260,8 +296,7 @@
std::string dataUpdatesDirName(dataZoneInfoDir);
dataUpdatesDirName += "/updates";
LOG(INFO) << "Removing: " << dataUpdatesDirName;
- bool deleted = deleteDir(dataUpdatesDirName);
- if (!deleted) {
+ if (!deleteDir(dataUpdatesDirName)) {
LOG(WARNING) << "Deletion of install metadata " << dataUpdatesDirName
<< " was not successful";
}
@@ -270,14 +305,151 @@
/*
* Deletes the timezone update distro directory.
*/
-static void deleteUpdateDistroDir(std::string& distroDirName) {
+static void deleteUpdateDistroDir(const std::string& distroDirName) {
LOG(INFO) << "Removing: " << distroDirName;
- bool deleted = deleteDir(distroDirName);
- if (!deleted) {
+ if (!deleteDir(distroDirName)) {
LOG(WARNING) << "Deletion of distro dir " << distroDirName << " was not successful";
}
}
+static void handleStagedUninstall(const std::string& dataStagedDirName,
+ const std::string& dataCurrentDirName,
+ const PathStatus dataCurrentDirStatus) {
+ LOG(INFO) << "Staged operation is an uninstall.";
+
+ // Delete the current install directory.
+ switch (dataCurrentDirStatus) {
+ case NONE:
+ // This is unexpected: No uninstall should be staged if there is nothing to
+ // uninstall. Carry on anyway.
+ LOG(WARNING) << "No current install to delete.";
+ break;
+ case IS_DIR:
+ // This is normal. Delete the current install dir.
+ if (!deleteDir(dataCurrentDirName)) {
+ LOG(WARNING) << "Deletion of current distro " << dataCurrentDirName
+ << " was not successful";
+ // If this happens we don't know whether we were able to delete or not. We don't
+ // delete the staged operation so it will be retried next boot unless overridden.
+ return;
+ }
+ break;
+ case IS_REG:
+ default:
+ // This is unexpected: We can try to delete the unexpected file and carry on.
+ LOG(WARNING) << "Current distro dir " << dataCurrentDirName
+ << " is not actually a directory. Attempting deletion.";
+ if (!deleteFile(dataCurrentDirName)) {
+ LOG(WARNING) << "Could not delete " << dataCurrentDirName;
+ return;
+ }
+ break;
+ }
+
+ // Delete the staged uninstall dir.
+ if (!deleteDir(dataStagedDirName)) {
+ LOG(WARNING) << "Deletion of current distro " << dataCurrentDirName
+ << " was not successful";
+ // If this happens we don't know whether we were able to delete the staged operation
+ // or not.
+ return;
+ }
+ LOG(INFO) << "Staged uninstall complete.";
+}
+
+static void handleStagedInstall(const std::string& dataStagedDirName,
+ const std::string& dataCurrentDirName,
+ const PathStatus dataCurrentDirStatus) {
+ LOG(INFO) << "Staged operation is an install.";
+
+ switch (dataCurrentDirStatus) {
+ case NONE:
+ // This is expected: This is the first install.
+ LOG(INFO) << "No current install to replace.";
+ break;
+ case IS_DIR:
+ // This is expected: We are replacing an existing install.
+ // Delete the current dir so we can replace it.
+ if (!deleteDir(dataCurrentDirName)) {
+ LOG(WARNING) << "Deletion of current distro " << dataCurrentDirName
+ << " was not successful";
+ // If this happens, we cannot proceed.
+ return;
+ }
+ break;
+ case IS_REG:
+ default:
+ // This is unexpected: We can try to delete the unexpected file and carry on.
+ LOG(WARNING) << "Current distro dir " << dataCurrentDirName
+ << " is not actually a directory. Attempting deletion.";
+ if (!deleteFile(dataCurrentDirName)) {
+ LOG(WARNING) << "Could not delete " << dataCurrentDirName;
+ return;
+ }
+ break;
+ }
+
+ // Move the staged dir so it is the new current dir, completing the install.
+ LOG(INFO) << "Moving " << dataStagedDirName << " to " << dataCurrentDirName;
+ int rc = rename(dataStagedDirName.c_str(), dataCurrentDirName.c_str());
+ if (rc == -1) {
+ PLOG(WARNING) << "Unable to rename directory from " << dataStagedDirName << " to "
+ << &dataCurrentDirName[0];
+ return;
+ }
+
+ LOG(INFO) << "Staged install complete.";
+}
+/*
+ * Process a staged operation if there is one.
+ */
+static void processStagedOperation(const std::string& dataStagedDirName,
+ const std::string& dataCurrentDirName) {
+ PathStatus dataStagedDirStatus = checkPath(dataStagedDirName);
+
+ // Exit early for the common case.
+ if (dataStagedDirStatus == NONE) {
+ LOG(DEBUG) << "No staged time zone operation.";
+ return;
+ }
+
+ // Check known directory names are in a good starting state.
+ if (dataStagedDirStatus != IS_DIR) {
+ LOG(WARNING) << "Staged distro dir " << dataStagedDirName
+ << " could not be accessed or is not a directory."
+ << " stagedDirStatus=" << dataStagedDirStatus;
+ return;
+ }
+
+ // dataStagedDirStatus == IS_DIR.
+
+ // Work out whether there is anything currently installed.
+ PathStatus dataCurrentDirStatus = checkPath(dataCurrentDirName);
+ if (dataCurrentDirStatus == ERR) {
+ LOG(WARNING) << "Current install dir " << dataCurrentDirName << " could not be accessed"
+ << " dataCurrentDirStatus=" << dataCurrentDirStatus;
+ return;
+ }
+
+ // We must perform the staged operation.
+
+ // Check to see if the staged directory contains an uninstall or an install operation.
+ std::string uninstallTombStoneFile(dataStagedDirName);
+ uninstallTombStoneFile += UNINSTALL_TOMBSTONE_FILE_NAME;
+ int uninstallTombStoneFileStatus = checkPath(uninstallTombStoneFile);
+ if (uninstallTombStoneFileStatus != IS_REG && uninstallTombStoneFileStatus != NONE) {
+ // Error case.
+ LOG(WARNING) << "Unable to determine if the staged operation is an uninstall.";
+ return;
+ }
+ if (uninstallTombStoneFileStatus == IS_REG) {
+ handleStagedUninstall(dataStagedDirName, dataCurrentDirName, dataCurrentDirStatus);
+ } else {
+ // uninstallTombStoneFileStatus == NONE meaning this is a staged install.
+ handleStagedInstall(dataStagedDirName, dataCurrentDirName, dataCurrentDirStatus);
+ }
+}
+
/*
* After a platform update it is likely that timezone data found on the system partition will be
* newer than the version found in the data partition. This tool detects this case and removes the
@@ -300,15 +472,25 @@
const char* systemZoneInfoDir = argv[1];
const char* dataZoneInfoDir = argv[2];
- // Check the distro directory exists. If it does not, exit quickly: nothing to do.
+ std::string dataStagedDirName(dataZoneInfoDir);
+ dataStagedDirName += STAGED_DIR_NAME;
+
std::string dataCurrentDirName(dataZoneInfoDir);
- dataCurrentDirName += "/current";
- int dataCurrentDirStatus = checkPath(dataCurrentDirName);
+ dataCurrentDirName += CURRENT_DIR_NAME;
+
+ // Check for an process any staged operation.
+ // If the staged operation could not be handled we still have to validate the current installed
+ // directory so we do not check for errors and do not quit early.
+ processStagedOperation(dataStagedDirName, dataCurrentDirName);
+
+ // Check the distro directory exists. If it does not, exit quickly: nothing to do.
+ PathStatus dataCurrentDirStatus = checkPath(dataCurrentDirName);
if (dataCurrentDirStatus == NONE) {
LOG(INFO) << "timezone distro dir " << dataCurrentDirName
<< " does not exist. No action required.";
return 0;
}
+
// If the distro directory path is not a directory or we can't stat() the path, exit with a
// warning: either there's a problem accessing storage or the world is not as it should be;
// nothing to do.