Merge "Allow native bridge to work without a code cache"
diff --git a/adb/adb_listeners.cpp b/adb/adb_listeners.cpp
index a335eec..641b537 100644
--- a/adb/adb_listeners.cpp
+++ b/adb/adb_listeners.cpp
@@ -118,7 +118,7 @@
return socket_loopback_server(port, SOCK_STREAM);
}
}
-#ifndef HAVE_WIN32_IPC /* no Unix-domain sockets on Win32 */
+#if !defined(_WIN32) // No Unix-domain sockets on Windows.
// It's nonsensical to support the "reserved" space on the adb host side
if (!strncmp(name, "local:", 6)) {
return socket_local_server(name + 6, ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM);
diff --git a/adb/protocol.txt b/adb/protocol.txt
index c9d3c24..333e7e7 100644
--- a/adb/protocol.txt
+++ b/adb/protocol.txt
@@ -133,7 +133,7 @@
---- WRITE(0, remote-id, "data") ----------------------------------------
+--- WRITE(local-id, remote-id, "data") ---------------------------------
The WRITE message sends data to the recipient's stream identified by
remote-id. The payload MUST be <= maxdata in length.
diff --git a/adb/remount_service.cpp b/adb/remount_service.cpp
index 3a4d2da..2893263 100644
--- a/adb/remount_service.cpp
+++ b/adb/remount_service.cpp
@@ -33,9 +33,12 @@
#include "adb_io.h"
#include "adb_utils.h"
#include "cutils/properties.h"
+#include "fs_mgr.h"
+
+const std::string kFstab_Prefix = "/fstab.";
// Returns the device used to mount a directory in /proc/mounts.
-static std::string find_mount(const char* dir) {
+static std::string find_proc_mount(const char* dir) {
std::unique_ptr<FILE, int(*)(FILE*)> fp(setmntent("/proc/mounts", "r"), endmntent);
if (!fp) {
return "";
@@ -50,6 +53,29 @@
return "";
}
+// Returns the device used to mount a directory in the fstab.
+static std::string find_fstab_mount(const char* dir) {
+ char propbuf[PROPERTY_VALUE_MAX];
+
+ property_get("ro.hardware", propbuf, "");
+ std::string fstab_filename = kFstab_Prefix + propbuf;
+ struct fstab* fstab = fs_mgr_read_fstab(fstab_filename.c_str());
+ struct fstab_rec* rec = fs_mgr_get_entry_for_mount_point(fstab, dir);
+ std::string dev = rec ? std::string(rec->blk_device) : "";
+ fs_mgr_free_fstab(fstab);
+ return dev;
+}
+
+// The proc entry for / is full of lies, so check fstab instead.
+// /proc/mounts lists rootfs and /dev/root, neither of which is what we want.
+static std::string find_mount(const char* dir) {
+ if (strcmp(dir, "/") == 0) {
+ return find_fstab_mount(dir);
+ } else {
+ return find_proc_mount(dir);
+ }
+}
+
bool make_block_device_writable(const std::string& dev) {
int fd = unix_open(dev.c_str(), O_RDONLY | O_CLOEXEC);
if (fd == -1) {
@@ -112,7 +138,13 @@
}
bool success = true;
- success &= remount_partition(fd, "/system");
+ property_get("ro.build.system_root_image", prop_buf, "");
+ bool system_root = !strcmp(prop_buf, "true");
+ if (system_root) {
+ success &= remount_partition(fd, "/");
+ } else {
+ success &= remount_partition(fd, "/system");
+ }
success &= remount_partition(fd, "/vendor");
success &= remount_partition(fd, "/oem");
diff --git a/debuggerd/Android.mk b/debuggerd/Android.mk
index 6cfb541..3fca709 100644
--- a/debuggerd/Android.mk
+++ b/debuggerd/Android.mk
@@ -77,12 +77,12 @@
debuggerd_test_src_files := \
utility.cpp \
- test/dump_maps_test.cpp \
test/dump_memory_test.cpp \
test/elf_fake.cpp \
test/log_fake.cpp \
test/property_fake.cpp \
test/ptrace_fake.cpp \
+ test/tombstone_test.cpp \
test/selinux_fake.cpp \
debuggerd_shared_libraries := \
diff --git a/debuggerd/arm/machine.cpp b/debuggerd/arm/machine.cpp
index b7d6997..78c2306 100644
--- a/debuggerd/arm/machine.cpp
+++ b/debuggerd/arm/machine.cpp
@@ -15,12 +15,15 @@
* limitations under the License.
*/
+#define LOG_TAG "DEBUG"
+
#include <errno.h>
#include <stdint.h>
#include <string.h>
#include <sys/ptrace.h>
#include <backtrace/Backtrace.h>
+#include <log/log.h>
#include "machine.h"
#include "utility.h"
@@ -28,7 +31,7 @@
void dump_memory_and_code(log_t* log, Backtrace* backtrace) {
pt_regs regs;
if (ptrace(PTRACE_GETREGS, backtrace->Tid(), 0, ®s)) {
- _LOG(log, logtype::ERROR, "cannot get registers: %s\n", strerror(errno));
+ ALOGE("cannot get registers: %s\n", strerror(errno));
return;
}
@@ -48,7 +51,7 @@
void dump_registers(log_t* log, pid_t tid) {
pt_regs r;
if (ptrace(PTRACE_GETREGS, tid, 0, &r)) {
- _LOG(log, logtype::ERROR, "cannot get registers: %s\n", strerror(errno));
+ ALOGE("cannot get registers: %s\n", strerror(errno));
return;
}
@@ -68,7 +71,7 @@
user_vfp vfp_regs;
if (ptrace(PTRACE_GETVFPREGS, tid, 0, &vfp_regs)) {
- _LOG(log, logtype::ERROR, "cannot get FP registers: %s\n", strerror(errno));
+ ALOGE("cannot get FP registers: %s\n", strerror(errno));
return;
}
diff --git a/debuggerd/arm64/machine.cpp b/debuggerd/arm64/machine.cpp
index 2e097da..e7bf79a 100644
--- a/debuggerd/arm64/machine.cpp
+++ b/debuggerd/arm64/machine.cpp
@@ -15,6 +15,8 @@
* limitations under the License.
*/
+#define LOG_TAG "DEBUG"
+
#include <elf.h>
#include <errno.h>
#include <stdint.h>
@@ -23,6 +25,7 @@
#include <sys/uio.h>
#include <backtrace/Backtrace.h>
+#include <log/log.h>
#include "machine.h"
#include "utility.h"
@@ -34,8 +37,7 @@
io.iov_len = sizeof(regs);
if (ptrace(PTRACE_GETREGSET, backtrace->Tid(), reinterpret_cast<void*>(NT_PRSTATUS), &io) == -1) {
- _LOG(log, logtype::ERROR, "%s: ptrace failed to get registers: %s",
- __func__, strerror(errno));
+ ALOGE("ptrace failed to get registers: %s", strerror(errno));
return;
}
@@ -57,7 +59,7 @@
io.iov_len = sizeof(r);
if (ptrace(PTRACE_GETREGSET, tid, (void*) NT_PRSTATUS, (void*) &io) == -1) {
- _LOG(log, logtype::ERROR, "ptrace error: %s\n", strerror(errno));
+ ALOGE("ptrace error: %s\n", strerror(errno));
return;
}
@@ -81,7 +83,7 @@
io.iov_len = sizeof(f);
if (ptrace(PTRACE_GETREGSET, tid, (void*) NT_PRFPREG, (void*) &io) == -1) {
- _LOG(log, logtype::ERROR, "ptrace error: %s\n", strerror(errno));
+ ALOGE("ptrace error: %s\n", strerror(errno));
return;
}
diff --git a/debuggerd/backtrace.cpp b/debuggerd/backtrace.cpp
index b8084c5..b46f8f4 100644
--- a/debuggerd/backtrace.cpp
+++ b/debuggerd/backtrace.cpp
@@ -105,7 +105,7 @@
}
if (!attached && ptrace(PTRACE_DETACH, tid, 0, 0) != 0) {
- _LOG(log, logtype::ERROR, "ptrace detach from %d failed: %s\n", tid, strerror(errno));
+ ALOGE("ptrace detach from %d failed: %s\n", tid, strerror(errno));
*detach_failed = true;
}
}
diff --git a/debuggerd/debuggerd.cpp b/debuggerd/debuggerd.cpp
index 26d6389..787b7aa 100644
--- a/debuggerd/debuggerd.cpp
+++ b/debuggerd/debuggerd.cpp
@@ -63,32 +63,18 @@
int32_t original_si_code;
};
-static void wait_for_user_action(const debugger_request_t &request) {
- // Find out the name of the process that crashed.
- char path[64];
- snprintf(path, sizeof(path), "/proc/%d/exe", request.pid);
-
- char exe[PATH_MAX];
- int count;
- if ((count = readlink(path, exe, sizeof(exe) - 1)) == -1) {
- ALOGE("readlink('%s') failed: %s", path, strerror(errno));
- strlcpy(exe, "unknown", sizeof(exe));
- } else {
- exe[count] = '\0';
- }
-
+static void wait_for_user_action(const debugger_request_t& request) {
// Explain how to attach the debugger.
- ALOGI("********************************************************\n"
+ ALOGI("***********************************************************\n"
"* Process %d has been suspended while crashing.\n"
- "* To attach gdbserver for a gdb connection on port 5039\n"
- "* and start gdbclient:\n"
+ "* To attach gdbserver and start gdb, run this on the host:\n"
"*\n"
- "* gdbclient %s :5039 %d\n"
+ "* gdbclient %d\n"
"*\n"
"* Wait for gdb to start, then press the VOLUME DOWN key\n"
"* to let the process continue crashing.\n"
- "********************************************************",
- request.pid, exe, request.tid);
+ "***********************************************************",
+ request.pid, request.tid);
// Wait for VOLUME DOWN.
if (init_getevent() == 0) {
@@ -250,10 +236,7 @@
static bool should_attach_gdb(debugger_request_t* request) {
if (request->action == DEBUGGER_ACTION_CRASH) {
- char value[PROPERTY_VALUE_MAX];
- property_get("debug.db.uid", value, "-1");
- int debug_uid = atoi(value);
- return debug_uid >= 0 && request->uid <= (uid_t)debug_uid;
+ return property_get_bool("debug.debuggerd.wait_for_gdb", false);
}
return false;
}
diff --git a/debuggerd/mips/machine.cpp b/debuggerd/mips/machine.cpp
index f7b8a86..cbf272a 100644
--- a/debuggerd/mips/machine.cpp
+++ b/debuggerd/mips/machine.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#define LOG_TAG "DEBUG"
+
#include <errno.h>
#include <inttypes.h>
#include <stdint.h>
@@ -21,6 +23,7 @@
#include <sys/ptrace.h>
#include <backtrace/Backtrace.h>
+#include <log/log.h>
#include "machine.h"
#include "utility.h"
@@ -32,7 +35,7 @@
void dump_memory_and_code(log_t* log, Backtrace* backtrace) {
pt_regs r;
if (ptrace(PTRACE_GETREGS, backtrace->Tid(), 0, &r)) {
- _LOG(log, logtype::ERROR, "cannot get registers: %s\n", strerror(errno));
+ ALOGE("cannot get registers: %s\n", strerror(errno));
return;
}
@@ -61,7 +64,7 @@
void dump_registers(log_t* log, pid_t tid) {
pt_regs r;
if(ptrace(PTRACE_GETREGS, tid, 0, &r)) {
- _LOG(log, logtype::ERROR, "cannot get registers: %s\n", strerror(errno));
+ ALOGE("cannot get registers: %s\n", strerror(errno));
return;
}
diff --git a/debuggerd/mips64/machine.cpp b/debuggerd/mips64/machine.cpp
index 293dcf6..0a8d532 100644
--- a/debuggerd/mips64/machine.cpp
+++ b/debuggerd/mips64/machine.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#define LOG_TAG "DEBUG"
+
#include <errno.h>
#include <inttypes.h>
#include <stdint.h>
@@ -21,6 +23,7 @@
#include <sys/ptrace.h>
#include <backtrace/Backtrace.h>
+#include <log/log.h>
#include "machine.h"
#include "utility.h"
@@ -32,7 +35,7 @@
void dump_memory_and_code(log_t* log, Backtrace* backtrace) {
pt_regs r;
if (ptrace(PTRACE_GETREGS, backtrace->Tid(), 0, &r)) {
- _LOG(log, logtype::ERROR, "cannot get registers: %s\n", strerror(errno));
+ ALOGE("cannot get registers: %s\n", strerror(errno));
return;
}
@@ -61,7 +64,7 @@
void dump_registers(log_t* log, pid_t tid) {
pt_regs r;
if(ptrace(PTRACE_GETREGS, tid, 0, &r)) {
- _LOG(log, logtype::ERROR, "cannot get registers: %s\n", strerror(errno));
+ ALOGE("cannot get registers: %s\n", strerror(errno));
return;
}
diff --git a/debuggerd/test/BacktraceMock.h b/debuggerd/test/BacktraceMock.h
index 5c252ab..f75534e 100644
--- a/debuggerd/test/BacktraceMock.h
+++ b/debuggerd/test/BacktraceMock.h
@@ -60,12 +60,14 @@
}
size_t bytes_available = buffer_.size() - offset;
- if (bytes_partial_read_ > 0) {
+ if (do_partial_read_) {
// Do a partial read.
if (bytes > bytes_partial_read_) {
bytes = bytes_partial_read_;
}
bytes_partial_read_ -= bytes;
+ // Only support a single partial read.
+ do_partial_read_ = false;
} else if (bytes > bytes_available) {
bytes = bytes_available;
}
@@ -82,6 +84,7 @@
buffer_.resize(bytes);
memcpy(buffer_.data(), buffer, bytes);
bytes_partial_read_ = 0;
+ do_partial_read_ = false;
last_read_addr_ = 0;
}
@@ -90,12 +93,14 @@
abort();
}
bytes_partial_read_ = bytes;
+ do_partial_read_ = true;
}
private:
std::vector<uint8_t> buffer_;
size_t bytes_partial_read_ = 0;
uintptr_t last_read_addr_ = 0;
+ bool do_partial_read_ = false;
};
#endif // _DEBUGGERD_TEST_BACKTRACE_MOCK_H
diff --git a/debuggerd/test/dump_memory_test.cpp b/debuggerd/test/dump_memory_test.cpp
index fcb0108..75e7028 100644
--- a/debuggerd/test/dump_memory_test.cpp
+++ b/debuggerd/test/dump_memory_test.cpp
@@ -288,9 +288,9 @@
ASSERT_STREQ(g_expected_partial_dump, tombstone_contents.c_str());
#if defined(__LP64__)
- ASSERT_STREQ("DEBUG Bytes read 102, is not a multiple of 8\n", getFakeLogPrint().c_str());
+ ASSERT_STREQ("6 DEBUG Bytes read 102, is not a multiple of 8\n", getFakeLogPrint().c_str());
#else
- ASSERT_STREQ("DEBUG Bytes read 102, is not a multiple of 4\n", getFakeLogPrint().c_str());
+ ASSERT_STREQ("6 DEBUG Bytes read 102, is not a multiple of 4\n", getFakeLogPrint().c_str());
#endif
// Verify that the log buf is empty, and no error messages.
@@ -313,12 +313,12 @@
ASSERT_STREQ(g_expected_partial_dump, tombstone_contents.c_str());
#if defined(__LP64__)
- ASSERT_STREQ("DEBUG Bytes read 45, is not a multiple of 8\n"
- "DEBUG Bytes after second read 106, is not a multiple of 8\n",
+ ASSERT_STREQ("6 DEBUG Bytes read 45, is not a multiple of 8\n"
+ "6 DEBUG Bytes after second read 106, is not a multiple of 8\n",
getFakeLogPrint().c_str());
#else
- ASSERT_STREQ("DEBUG Bytes read 45, is not a multiple of 4\n"
- "DEBUG Bytes after second read 106, is not a multiple of 4\n",
+ ASSERT_STREQ("6 DEBUG Bytes read 45, is not a multiple of 4\n"
+ "6 DEBUG Bytes after second read 106, is not a multiple of 4\n",
getFakeLogPrint().c_str());
#endif
@@ -502,3 +502,239 @@
ASSERT_STREQ("", getFakeLogBuf().c_str());
ASSERT_STREQ("", getFakeLogPrint().c_str());
}
+
+TEST_F(DumpMemoryTest, first_read_empty) {
+ uint8_t buffer[256];
+ for (size_t i = 0; i < sizeof(buffer); i++) {
+ buffer[i] = i;
+ }
+ backtrace_mock_->SetReadData(buffer, sizeof(buffer));
+ backtrace_mock_->SetPartialReadAmount(0);
+
+ size_t page_size = sysconf(_SC_PAGE_SIZE);
+ uintptr_t addr = 0x10000020 + page_size - 120;
+ dump_memory(&log_, backtrace_mock_.get(), addr, "memory near %.2s:", "r4");
+
+ std::string tombstone_contents;
+ ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+ ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+ const char* expected_dump = \
+"\nmemory near r4:\n"
+#if defined(__LP64__)
+" 0000000010000f88 ---------------- ---------------- ................\n"
+" 0000000010000f98 ---------------- ---------------- ................\n"
+" 0000000010000fa8 ---------------- ---------------- ................\n"
+" 0000000010000fb8 ---------------- ---------------- ................\n"
+" 0000000010000fc8 ---------------- ---------------- ................\n"
+" 0000000010000fd8 ---------------- ---------------- ................\n"
+" 0000000010000fe8 ---------------- ---------------- ................\n"
+" 0000000010000ff8 ---------------- 7f7e7d7c7b7a7978 ........xyz{|}~.\n"
+" 0000000010001008 8786858483828180 8f8e8d8c8b8a8988 ................\n"
+" 0000000010001018 9796959493929190 9f9e9d9c9b9a9998 ................\n"
+" 0000000010001028 a7a6a5a4a3a2a1a0 afaeadacabaaa9a8 ................\n"
+" 0000000010001038 b7b6b5b4b3b2b1b0 bfbebdbcbbbab9b8 ................\n"
+" 0000000010001048 c7c6c5c4c3c2c1c0 cfcecdcccbcac9c8 ................\n"
+" 0000000010001058 d7d6d5d4d3d2d1d0 dfdedddcdbdad9d8 ................\n"
+" 0000000010001068 e7e6e5e4e3e2e1e0 efeeedecebeae9e8 ................\n"
+" 0000000010001078 f7f6f5f4f3f2f1f0 fffefdfcfbfaf9f8 ................\n";
+#else
+" 10000f88 -------- -------- -------- -------- ................\n"
+" 10000f98 -------- -------- -------- -------- ................\n"
+" 10000fa8 -------- -------- -------- -------- ................\n"
+" 10000fb8 -------- -------- -------- -------- ................\n"
+" 10000fc8 -------- -------- -------- -------- ................\n"
+" 10000fd8 -------- -------- -------- -------- ................\n"
+" 10000fe8 -------- -------- -------- -------- ................\n"
+" 10000ff8 -------- -------- 7b7a7978 7f7e7d7c ........xyz{|}~.\n"
+" 10001008 83828180 87868584 8b8a8988 8f8e8d8c ................\n"
+" 10001018 93929190 97969594 9b9a9998 9f9e9d9c ................\n"
+" 10001028 a3a2a1a0 a7a6a5a4 abaaa9a8 afaeadac ................\n"
+" 10001038 b3b2b1b0 b7b6b5b4 bbbab9b8 bfbebdbc ................\n"
+" 10001048 c3c2c1c0 c7c6c5c4 cbcac9c8 cfcecdcc ................\n"
+" 10001058 d3d2d1d0 d7d6d5d4 dbdad9d8 dfdedddc ................\n"
+" 10001068 e3e2e1e0 e7e6e5e4 ebeae9e8 efeeedec ................\n"
+" 10001078 f3f2f1f0 f7f6f5f4 fbfaf9f8 fffefdfc ................\n";
+#endif
+ ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
+
+ // Verify that the log buf is empty, and no error messages.
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(DumpMemoryTest, first_read_empty_second_read_stops) {
+ uint8_t buffer[224];
+ for (size_t i = 0; i < sizeof(buffer); i++) {
+ buffer[i] = i;
+ }
+ backtrace_mock_->SetReadData(buffer, sizeof(buffer));
+ backtrace_mock_->SetPartialReadAmount(0);
+
+ size_t page_size = sysconf(_SC_PAGE_SIZE);
+ uintptr_t addr = 0x10000020 + page_size - 192;
+ dump_memory(&log_, backtrace_mock_.get(), addr, "memory near %.2s:", "r4");
+
+ std::string tombstone_contents;
+ ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+ ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+ const char* expected_dump = \
+"\nmemory near r4:\n"
+#if defined(__LP64__)
+" 0000000010000f40 ---------------- ---------------- ................\n"
+" 0000000010000f50 ---------------- ---------------- ................\n"
+" 0000000010000f60 ---------------- ---------------- ................\n"
+" 0000000010000f70 ---------------- ---------------- ................\n"
+" 0000000010000f80 ---------------- ---------------- ................\n"
+" 0000000010000f90 ---------------- ---------------- ................\n"
+" 0000000010000fa0 ---------------- ---------------- ................\n"
+" 0000000010000fb0 ---------------- ---------------- ................\n"
+" 0000000010000fc0 ---------------- ---------------- ................\n"
+" 0000000010000fd0 ---------------- ---------------- ................\n"
+" 0000000010000fe0 ---------------- ---------------- ................\n"
+" 0000000010000ff0 ---------------- ---------------- ................\n"
+" 0000000010001000 c7c6c5c4c3c2c1c0 cfcecdcccbcac9c8 ................\n"
+" 0000000010001010 d7d6d5d4d3d2d1d0 dfdedddcdbdad9d8 ................\n"
+" 0000000010001020 ---------------- ---------------- ................\n"
+" 0000000010001030 ---------------- ---------------- ................\n";
+#else
+" 10000f40 -------- -------- -------- -------- ................\n"
+" 10000f50 -------- -------- -------- -------- ................\n"
+" 10000f60 -------- -------- -------- -------- ................\n"
+" 10000f70 -------- -------- -------- -------- ................\n"
+" 10000f80 -------- -------- -------- -------- ................\n"
+" 10000f90 -------- -------- -------- -------- ................\n"
+" 10000fa0 -------- -------- -------- -------- ................\n"
+" 10000fb0 -------- -------- -------- -------- ................\n"
+" 10000fc0 -------- -------- -------- -------- ................\n"
+" 10000fd0 -------- -------- -------- -------- ................\n"
+" 10000fe0 -------- -------- -------- -------- ................\n"
+" 10000ff0 -------- -------- -------- -------- ................\n"
+" 10001000 c3c2c1c0 c7c6c5c4 cbcac9c8 cfcecdcc ................\n"
+" 10001010 d3d2d1d0 d7d6d5d4 dbdad9d8 dfdedddc ................\n"
+" 10001020 -------- -------- -------- -------- ................\n"
+" 10001030 -------- -------- -------- -------- ................\n";
+#endif
+ ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
+
+ // Verify that the log buf is empty, and no error messages.
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(DumpMemoryTest, first_read_empty_next_page_out_of_range) {
+ uint8_t buffer[256];
+ for (size_t i = 0; i < sizeof(buffer); i++) {
+ buffer[i] = i;
+ }
+ backtrace_mock_->SetReadData(buffer, sizeof(buffer));
+ backtrace_mock_->SetPartialReadAmount(0);
+
+ uintptr_t addr = 0x10000020;
+ dump_memory(&log_, backtrace_mock_.get(), addr, "memory near %.2s:", "r4");
+
+ std::string tombstone_contents;
+ ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+ ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+ const char* expected_dump = \
+"\nmemory near r4:\n"
+#if defined(__LP64__)
+" 0000000010000000 ---------------- ---------------- ................\n"
+" 0000000010000010 ---------------- ---------------- ................\n"
+" 0000000010000020 ---------------- ---------------- ................\n"
+" 0000000010000030 ---------------- ---------------- ................\n"
+" 0000000010000040 ---------------- ---------------- ................\n"
+" 0000000010000050 ---------------- ---------------- ................\n"
+" 0000000010000060 ---------------- ---------------- ................\n"
+" 0000000010000070 ---------------- ---------------- ................\n"
+" 0000000010000080 ---------------- ---------------- ................\n"
+" 0000000010000090 ---------------- ---------------- ................\n"
+" 00000000100000a0 ---------------- ---------------- ................\n"
+" 00000000100000b0 ---------------- ---------------- ................\n"
+" 00000000100000c0 ---------------- ---------------- ................\n"
+" 00000000100000d0 ---------------- ---------------- ................\n"
+" 00000000100000e0 ---------------- ---------------- ................\n"
+" 00000000100000f0 ---------------- ---------------- ................\n";
+#else
+" 10000000 -------- -------- -------- -------- ................\n"
+" 10000010 -------- -------- -------- -------- ................\n"
+" 10000020 -------- -------- -------- -------- ................\n"
+" 10000030 -------- -------- -------- -------- ................\n"
+" 10000040 -------- -------- -------- -------- ................\n"
+" 10000050 -------- -------- -------- -------- ................\n"
+" 10000060 -------- -------- -------- -------- ................\n"
+" 10000070 -------- -------- -------- -------- ................\n"
+" 10000080 -------- -------- -------- -------- ................\n"
+" 10000090 -------- -------- -------- -------- ................\n"
+" 100000a0 -------- -------- -------- -------- ................\n"
+" 100000b0 -------- -------- -------- -------- ................\n"
+" 100000c0 -------- -------- -------- -------- ................\n"
+" 100000d0 -------- -------- -------- -------- ................\n"
+" 100000e0 -------- -------- -------- -------- ................\n"
+" 100000f0 -------- -------- -------- -------- ................\n";
+#endif
+ ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
+
+ // Verify that the log buf is empty, and no error messages.
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(DumpMemoryTest, first_read_empty_next_page_out_of_range_fence_post) {
+ uint8_t buffer[256];
+ for (size_t i = 0; i < sizeof(buffer); i++) {
+ buffer[i] = i;
+ }
+ backtrace_mock_->SetReadData(buffer, sizeof(buffer));
+ backtrace_mock_->SetPartialReadAmount(0);
+
+ size_t page_size = sysconf(_SC_PAGE_SIZE);
+ uintptr_t addr = 0x10000020 + page_size - 256;
+
+ dump_memory(&log_, backtrace_mock_.get(), addr, "memory near %.2s:", "r4");
+
+ std::string tombstone_contents;
+ ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+ ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+ const char* expected_dump = \
+"\nmemory near r4:\n"
+#if defined(__LP64__)
+" 0000000010000f00 ---------------- ---------------- ................\n"
+" 0000000010000f10 ---------------- ---------------- ................\n"
+" 0000000010000f20 ---------------- ---------------- ................\n"
+" 0000000010000f30 ---------------- ---------------- ................\n"
+" 0000000010000f40 ---------------- ---------------- ................\n"
+" 0000000010000f50 ---------------- ---------------- ................\n"
+" 0000000010000f60 ---------------- ---------------- ................\n"
+" 0000000010000f70 ---------------- ---------------- ................\n"
+" 0000000010000f80 ---------------- ---------------- ................\n"
+" 0000000010000f90 ---------------- ---------------- ................\n"
+" 0000000010000fa0 ---------------- ---------------- ................\n"
+" 0000000010000fb0 ---------------- ---------------- ................\n"
+" 0000000010000fc0 ---------------- ---------------- ................\n"
+" 0000000010000fd0 ---------------- ---------------- ................\n"
+" 0000000010000fe0 ---------------- ---------------- ................\n"
+" 0000000010000ff0 ---------------- ---------------- ................\n";
+#else
+" 10000f00 -------- -------- -------- -------- ................\n"
+" 10000f10 -------- -------- -------- -------- ................\n"
+" 10000f20 -------- -------- -------- -------- ................\n"
+" 10000f30 -------- -------- -------- -------- ................\n"
+" 10000f40 -------- -------- -------- -------- ................\n"
+" 10000f50 -------- -------- -------- -------- ................\n"
+" 10000f60 -------- -------- -------- -------- ................\n"
+" 10000f70 -------- -------- -------- -------- ................\n"
+" 10000f80 -------- -------- -------- -------- ................\n"
+" 10000f90 -------- -------- -------- -------- ................\n"
+" 10000fa0 -------- -------- -------- -------- ................\n"
+" 10000fb0 -------- -------- -------- -------- ................\n"
+" 10000fc0 -------- -------- -------- -------- ................\n"
+" 10000fd0 -------- -------- -------- -------- ................\n"
+" 10000fe0 -------- -------- -------- -------- ................\n"
+" 10000ff0 -------- -------- -------- -------- ................\n";
+#endif
+ ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
+
+ // Verify that the log buf is empty, and no error messages.
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
diff --git a/debuggerd/test/log_fake.cpp b/debuggerd/test/log_fake.cpp
index 26523ad..d584a5e 100644
--- a/debuggerd/test/log_fake.cpp
+++ b/debuggerd/test/log_fake.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <errno.h>
#include <stdarg.h>
#include <string>
@@ -44,14 +45,16 @@
return g_fake_log_print;
}
-extern "C" int __android_log_buf_write(int, int, const char* tag, const char* msg) {
+extern "C" int __android_log_buf_write(int bufId, int prio, const char* tag, const char* msg) {
+ g_fake_log_buf += std::to_string(bufId) + ' ' + std::to_string(prio) + ' ';
g_fake_log_buf += tag;
g_fake_log_buf += ' ';
g_fake_log_buf += msg;
return 1;
}
-extern "C" int __android_log_print(int, const char* tag, const char* fmt, ...) {
+extern "C" int __android_log_print(int prio, const char* tag, const char* fmt, ...) {
+ g_fake_log_print += std::to_string(prio) + ' ';
g_fake_log_print += tag;
g_fake_log_print += ' ';
@@ -70,6 +73,7 @@
}
extern "C" struct logger_list* android_logger_list_open(log_id_t, int, unsigned int, pid_t) {
+ errno = EACCES;
return nullptr;
}
diff --git a/debuggerd/test/dump_maps_test.cpp b/debuggerd/test/tombstone_test.cpp
similarity index 90%
rename from debuggerd/test/dump_maps_test.cpp
rename to debuggerd/test/tombstone_test.cpp
index 230f4f5..a771a39 100644
--- a/debuggerd/test/dump_maps_test.cpp
+++ b/debuggerd/test/tombstone_test.cpp
@@ -45,7 +45,7 @@
void dump_backtrace_to_log(Backtrace*, log_t*, char const*) {
}
-class DumpMapsTest : public ::testing::Test {
+class TombstoneTest : public ::testing::Test {
protected:
virtual void SetUp() {
map_mock_.reset(new BacktraceMapMock());
@@ -92,7 +92,7 @@
log_t log_;
};
-TEST_F(DumpMapsTest, single_map) {
+TEST_F(TombstoneTest, single_map) {
backtrace_map_t map;
#if defined(__LP64__)
map.start = 0x123456789abcd000UL;
@@ -122,7 +122,7 @@
ASSERT_STREQ("", getFakeLogPrint().c_str());
}
-TEST_F(DumpMapsTest, single_map_elf_build_id) {
+TEST_F(TombstoneTest, single_map_elf_build_id) {
backtrace_map_t map;
#if defined(__LP64__)
map.start = 0x123456789abcd000UL;
@@ -157,7 +157,7 @@
// Even though build id is present, it should not be printed in either of
// these cases.
-TEST_F(DumpMapsTest, single_map_no_build_id) {
+TEST_F(TombstoneTest, single_map_no_build_id) {
backtrace_map_t map;
#if defined(__LP64__)
map.start = 0x123456789abcd000UL;
@@ -194,7 +194,7 @@
ASSERT_STREQ("", getFakeLogPrint().c_str());
}
-TEST_F(DumpMapsTest, multiple_maps) {
+TEST_F(TombstoneTest, multiple_maps) {
backtrace_map_t map;
map.start = 0xa234000;
@@ -256,7 +256,7 @@
ASSERT_STREQ("", getFakeLogPrint().c_str());
}
-TEST_F(DumpMapsTest, multiple_maps_fault_address_before) {
+TEST_F(TombstoneTest, multiple_maps_fault_address_before) {
backtrace_map_t map;
map.start = 0xa434000;
@@ -310,7 +310,7 @@
ASSERT_STREQ("", getFakeLogPrint().c_str());
}
-TEST_F(DumpMapsTest, multiple_maps_fault_address_between) {
+TEST_F(TombstoneTest, multiple_maps_fault_address_between) {
backtrace_map_t map;
map.start = 0xa434000;
@@ -364,7 +364,7 @@
ASSERT_STREQ("", getFakeLogPrint().c_str());
}
-TEST_F(DumpMapsTest, multiple_maps_fault_address_in_map) {
+TEST_F(TombstoneTest, multiple_maps_fault_address_in_map) {
backtrace_map_t map;
map.start = 0xa434000;
@@ -416,7 +416,7 @@
ASSERT_STREQ("", getFakeLogPrint().c_str());
}
-TEST_F(DumpMapsTest, multiple_maps_fault_address_after) {
+TEST_F(TombstoneTest, multiple_maps_fault_address_after) {
backtrace_map_t map;
map.start = 0xa434000;
@@ -474,7 +474,7 @@
ASSERT_STREQ("", getFakeLogPrint().c_str());
}
-TEST_F(DumpMapsTest, multiple_maps_getsiginfo_fail) {
+TEST_F(TombstoneTest, multiple_maps_getsiginfo_fail) {
backtrace_map_t map;
map.start = 0xa434000;
@@ -493,7 +493,6 @@
ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
const char* expected_dump = \
-"Cannot get siginfo for 100: Bad address\n"
"\nmemory map:\n"
#if defined(__LP64__)
" 00000000'0a434000-00000000'0a434fff -w- 1000 1000 (load base 0xd000)\n";
@@ -503,12 +502,11 @@
ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
// Verify that the log buf is empty, and no error messages.
- ASSERT_STREQ("DEBUG Cannot get siginfo for 100: Bad address\n",
- getFakeLogBuf().c_str());
- ASSERT_STREQ("", getFakeLogPrint().c_str());
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("6 DEBUG Cannot get siginfo for 100: Bad address\n\n", getFakeLogPrint().c_str());
}
-TEST_F(DumpMapsTest, multiple_maps_check_signal_has_si_addr) {
+TEST_F(TombstoneTest, multiple_maps_check_signal_has_si_addr) {
backtrace_map_t map;
map.start = 0xa434000;
@@ -569,3 +567,33 @@
ASSERT_STREQ("", getFakeLogPrint().c_str());
}
}
+
+TEST_F(TombstoneTest, dump_signal_info_error) {
+ siginfo_t si;
+ si.si_signo = 0;
+ ptrace_set_fake_getsiginfo(si);
+
+ dump_signal_info(&log_, 123, SIGSEGV, 10);
+
+ std::string tombstone_contents;
+ ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+ ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+ ASSERT_STREQ("", tombstone_contents.c_str());
+
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("6 DEBUG cannot get siginfo: Bad address\n\n", getFakeLogPrint().c_str());
+}
+
+TEST_F(TombstoneTest, dump_log_file_error) {
+ log_.should_retrieve_logcat = true;
+ dump_log_file(&log_, 123, "/fake/filename", 10);
+
+ std::string tombstone_contents;
+ ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+ ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+ ASSERT_STREQ("", tombstone_contents.c_str());
+
+ ASSERT_STREQ("", getFakeLogBuf().c_str());
+ ASSERT_STREQ("6 DEBUG Unable to open /fake/filename: Permission denied\n\n",
+ getFakeLogPrint().c_str());
+}
diff --git a/debuggerd/tombstone.cpp b/debuggerd/tombstone.cpp
index b0ad274..114c7e4 100644
--- a/debuggerd/tombstone.cpp
+++ b/debuggerd/tombstone.cpp
@@ -180,7 +180,7 @@
siginfo_t si;
memset(&si, 0, sizeof(si));
if (ptrace(PTRACE_GETSIGINFO, tid, 0, &si) == -1) {
- _LOG(log, logtype::HEADER, "cannot get siginfo: %s\n", strerror(errno));
+ ALOGE("cannot get siginfo: %s\n", strerror(errno));
return;
}
@@ -338,7 +338,7 @@
print_fault_address_marker = signal_has_si_addr(si.si_signo);
addr = reinterpret_cast<uintptr_t>(si.si_addr);
} else {
- _LOG(log, logtype::ERROR, "Cannot get siginfo for %d: %s\n", tid, strerror(errno));
+ ALOGE("Cannot get siginfo for %d: %s\n", tid, strerror(errno));
}
_LOG(log, logtype::MAPS, "\n");
@@ -448,7 +448,7 @@
// Skip this thread if cannot ptrace it
if (ptrace(PTRACE_ATTACH, new_tid, 0, 0) < 0) {
- _LOG(log, logtype::ERROR, "ptrace attach to %d failed: %s\n", new_tid, strerror(errno));
+ ALOGE("ptrace attach to %d failed: %s\n", new_tid, strerror(errno));
continue;
}
@@ -471,7 +471,7 @@
log->current_tid = log->crashed_tid;
if (ptrace(PTRACE_DETACH, new_tid, 0, 0) != 0) {
- _LOG(log, logtype::ERROR, "ptrace detach from %d failed: %s\n", new_tid, strerror(errno));
+ ALOGE("ptrace detach from %d failed: %s\n", new_tid, strerror(errno));
detach_failed = true;
}
}
@@ -517,13 +517,11 @@
// non-blocking EOF; we're done
break;
} else {
- _LOG(log, logtype::ERROR, "Error while reading log: %s\n",
- strerror(-actual));
+ ALOGE("Error while reading log: %s\n", strerror(-actual));
break;
}
} else if (actual == 0) {
- _LOG(log, logtype::ERROR, "Got zero bytes while reading log: %s\n",
- strerror(errno));
+ ALOGE("Got zero bytes while reading log: %s\n", strerror(errno));
break;
}
@@ -789,11 +787,11 @@
log.crashed_tid = tid;
if ((mkdir(TOMBSTONE_DIR, 0755) == -1) && (errno != EEXIST)) {
- _LOG(&log, logtype::ERROR, "failed to create %s: %s\n", TOMBSTONE_DIR, strerror(errno));
+ ALOGE("failed to create %s: %s\n", TOMBSTONE_DIR, strerror(errno));
}
if (chown(TOMBSTONE_DIR, AID_SYSTEM, AID_SYSTEM) == -1) {
- _LOG(&log, logtype::ERROR, "failed to change ownership of %s: %s\n", TOMBSTONE_DIR, strerror(errno));
+ ALOGE("failed to change ownership of %s: %s\n", TOMBSTONE_DIR, strerror(errno));
}
int fd = -1;
@@ -801,11 +799,11 @@
if (selinux_android_restorecon(TOMBSTONE_DIR, 0) == 0) {
path = find_and_open_tombstone(&fd);
} else {
- _LOG(&log, logtype::ERROR, "Failed to restore security context, not writing tombstone.\n");
+ ALOGE("Failed to restore security context, not writing tombstone.\n");
}
if (fd < 0) {
- _LOG(&log, logtype::ERROR, "Skipping tombstone write, nothing to do.\n");
+ ALOGE("Skipping tombstone write, nothing to do.\n");
*detach_failed = false;
return NULL;
}
diff --git a/debuggerd/utility.cpp b/debuggerd/utility.cpp
index 9f340a8..f5d6ec1 100644
--- a/debuggerd/utility.cpp
+++ b/debuggerd/utility.cpp
@@ -35,8 +35,7 @@
// Whitelist output desired in the logcat output.
bool is_allowed_in_logcat(enum logtype ltype) {
- if ((ltype == ERROR)
- || (ltype == HEADER)
+ if ((ltype == HEADER)
|| (ltype == REGISTERS)
|| (ltype == BACKTRACE)) {
return true;
@@ -157,13 +156,28 @@
bytes &= ~(sizeof(uintptr_t) - 1);
}
- if (bytes < MEMORY_BYTES_TO_DUMP && bytes > 0) {
- // Try to do one more read. This could happen if a read crosses a map, but
- // the maps do not have any break between them. Only requires one extra
- // read because a map has to contain at least one page, and the total
- // number of bytes to dump is smaller than a page.
- size_t bytes2 = backtrace->Read(addr + bytes, reinterpret_cast<uint8_t*>(data) + bytes,
- sizeof(data) - bytes);
+ uintptr_t start = 0;
+ bool skip_2nd_read = false;
+ if (bytes == 0) {
+ // In this case, we might want to try another read at the beginning of
+ // the next page only if it's within the amount of memory we would have
+ // read.
+ size_t page_size = sysconf(_SC_PAGE_SIZE);
+ start = ((addr + (page_size - 1)) & ~(page_size - 1)) - addr;
+ if (start == 0 || start >= MEMORY_BYTES_TO_DUMP) {
+ skip_2nd_read = true;
+ }
+ }
+
+ if (bytes < MEMORY_BYTES_TO_DUMP && !skip_2nd_read) {
+ // Try to do one more read. This could happen if a read crosses a map,
+ // but the maps do not have any break between them. Or it could happen
+ // if reading from an unreadable map, but the read would cross back
+ // into a readable map. Only requires one extra read because a map has
+ // to contain at least one page, and the total number of bytes to dump
+ // is smaller than a page.
+ size_t bytes2 = backtrace->Read(addr + start + bytes, reinterpret_cast<uint8_t*>(data) + bytes,
+ sizeof(data) - bytes - start);
bytes += bytes2;
if (bytes2 > 0 && bytes % sizeof(uintptr_t) != 0) {
// This should never happen, but we'll try and continue any way.
@@ -179,15 +193,16 @@
// On 32-bit machines, there are still 16 bytes per line but addresses and
// words are of course presented differently.
uintptr_t* data_ptr = data;
+ size_t current = 0;
+ size_t total_bytes = start + bytes;
for (size_t line = 0; line < MEMORY_BYTES_TO_DUMP / MEMORY_BYTES_PER_LINE; line++) {
std::string logline;
android::base::StringAppendF(&logline, " %" PRIPTR, addr);
addr += MEMORY_BYTES_PER_LINE;
std::string ascii;
- for (size_t i = 0; i < MEMORY_BYTES_PER_LINE / sizeof(uintptr_t); i++, data_ptr++) {
- if (bytes >= sizeof(uintptr_t)) {
- bytes -= sizeof(uintptr_t);
+ for (size_t i = 0; i < MEMORY_BYTES_PER_LINE / sizeof(uintptr_t); i++) {
+ if (current >= start && current + sizeof(uintptr_t) <= total_bytes) {
android::base::StringAppendF(&logline, " %" PRIPTR, *data_ptr);
// Fill out the ascii string from the data.
@@ -199,10 +214,12 @@
ascii += '.';
}
}
+ data_ptr++;
} else {
logline += ' ' + std::string(sizeof(uintptr_t) * 2, '-');
ascii += std::string(sizeof(uintptr_t), '.');
}
+ current += sizeof(uintptr_t);
}
_LOG(log, logtype::MEMORY, "%s %s\n", logline.c_str(), ascii.c_str());
}
diff --git a/debuggerd/utility.h b/debuggerd/utility.h
index 263374d..8bef192 100644
--- a/debuggerd/utility.h
+++ b/debuggerd/utility.h
@@ -59,7 +59,6 @@
// List of types of logs to simplify the logging decision in _LOG
enum logtype {
- ERROR,
HEADER,
THREAD,
REGISTERS,
diff --git a/debuggerd/x86/machine.cpp b/debuggerd/x86/machine.cpp
index c5f9259..af10817 100644
--- a/debuggerd/x86/machine.cpp
+++ b/debuggerd/x86/machine.cpp
@@ -14,12 +14,15 @@
* limitations under the License.
*/
+#define LOG_TAG "DEBUG"
+
#include <errno.h>
#include <stdint.h>
#include <string.h>
#include <sys/ptrace.h>
#include <backtrace/Backtrace.h>
+#include <log/log.h>
#include "machine.h"
#include "utility.h"
@@ -27,7 +30,7 @@
void dump_memory_and_code(log_t* log, Backtrace* backtrace) {
struct pt_regs r;
if (ptrace(PTRACE_GETREGS, backtrace->Tid(), 0, &r) == -1) {
- _LOG(log, logtype::ERROR, "cannot get registers: %s\n", strerror(errno));
+ ALOGE("cannot get registers: %s\n", strerror(errno));
return;
}
@@ -44,7 +47,7 @@
void dump_registers(log_t* log, pid_t tid) {
struct pt_regs r;
if (ptrace(PTRACE_GETREGS, tid, 0, &r) == -1) {
- _LOG(log, logtype::ERROR, "cannot get registers: %s\n", strerror(errno));
+ ALOGE("cannot get registers: %s\n", strerror(errno));
return;
}
diff --git a/debuggerd/x86_64/machine.cpp b/debuggerd/x86_64/machine.cpp
index 4f09a5d..fc86bc2 100644
--- a/debuggerd/x86_64/machine.cpp
+++ b/debuggerd/x86_64/machine.cpp
@@ -14,6 +14,8 @@
** limitations under the License.
*/
+#define LOG_TAG "DEBUG"
+
#include <errno.h>
#include <stdint.h>
#include <sys/ptrace.h>
@@ -21,6 +23,7 @@
#include <sys/user.h>
#include <backtrace/Backtrace.h>
+#include <log/log.h>
#include "machine.h"
#include "utility.h"
@@ -28,7 +31,7 @@
void dump_memory_and_code(log_t* log, Backtrace* backtrace) {
struct user_regs_struct r;
if (ptrace(PTRACE_GETREGS, backtrace->Tid(), 0, &r) == -1) {
- _LOG(log, logtype::ERROR, "cannot get registers: %s\n", strerror(errno));
+ ALOGE("cannot get registers: %s\n", strerror(errno));
return;
}
@@ -45,7 +48,7 @@
void dump_registers(log_t* log, pid_t tid) {
struct user_regs_struct r;
if (ptrace(PTRACE_GETREGS, tid, 0, &r) == -1) {
- _LOG(log, logtype::ERROR, "cannot get registers: %s\n", strerror(errno));
+ ALOGE("cannot get registers: %s\n", strerror(errno));
return;
}
diff --git a/libbacktrace/backtrace_test.cpp b/libbacktrace/backtrace_test.cpp
index 760f5cc..6bd7529 100644
--- a/libbacktrace/backtrace_test.cpp
+++ b/libbacktrace/backtrace_test.cpp
@@ -974,8 +974,8 @@
<< "Offset at " << i << " length " << j << " wrote too much data";
}
}
- delete data;
- delete expected;
+ delete[] data;
+ delete[] expected;
}
TEST(libbacktrace, thread_read) {
diff --git a/libsparse/sparse_crc32.h b/libsparse/sparse_crc32.h
index cad8a86..50cd9e9 100644
--- a/libsparse/sparse_crc32.h
+++ b/libsparse/sparse_crc32.h
@@ -14,7 +14,19 @@
* limitations under the License.
*/
+#ifndef _LIBSPARSE_SPARSE_CRC32_H_
+#define _LIBSPARSE_SPARSE_CRC32_H_
+
#include <stdint.h>
+#ifdef __cplusplus
+extern "C" {
+#endif
+
uint32_t sparse_crc32(uint32_t crc, const void *buf, size_t size);
+#ifdef __cplusplus
+}
+#endif
+
+#endif