Remove redundant action triggers on persist.sys.usb.config
am: 05e04a134e
Change-Id: I7d94aa8729514e7ce1e4fac301c9d52fa7dc66bc
diff --git a/adb/Android.mk b/adb/Android.mk
index 695cbe0..8f56d74 100644
--- a/adb/Android.mk
+++ b/adb/Android.mk
@@ -62,6 +62,7 @@
fdevent_test.cpp \
socket_test.cpp \
sysdeps_test.cpp \
+ sysdeps/stat_test.cpp \
transport_test.cpp \
LIBADB_CFLAGS := \
@@ -89,6 +90,7 @@
LIBADB_windows_SRC_FILES := \
sysdeps_win32.cpp \
+ sysdeps/win32/stat.cpp \
usb_windows.cpp \
LIBADB_TEST_windows_SRCS := \
diff --git a/adb/adb_utils_test.cpp b/adb/adb_utils_test.cpp
index f1ebaa1..a78f8d3 100644
--- a/adb/adb_utils_test.cpp
+++ b/adb/adb_utils_test.cpp
@@ -52,18 +52,6 @@
ASSERT_TRUE(directory_exists(profiles_dir));
- // On modern (English?) Windows, this is a directory symbolic link to
- // C:\ProgramData. Symbolic links are rare on Windows and the user requires
- // a special permission (by default granted to Administrative users) to
- // create symbolic links.
- ASSERT_FALSE(directory_exists(subdir(profiles_dir, "All Users")));
-
- // On modern (English?) Windows, this is a directory junction to
- // C:\Users\Default. Junctions are used throughout user profile directories
- // for backwards compatibility and they don't require any special permissions
- // to create.
- ASSERT_FALSE(directory_exists(subdir(profiles_dir, "Default User")));
-
ASSERT_FALSE(directory_exists(subdir(profiles_dir, "does-not-exist")));
#else
ASSERT_TRUE(directory_exists("/proc"));
@@ -72,6 +60,28 @@
#endif
}
+#if defined(_WIN32)
+TEST(adb_utils, directory_exists_win32_symlink_junction) {
+ char profiles_dir[MAX_PATH];
+ DWORD cch = arraysize(profiles_dir);
+
+ // On typical Windows 7, returns C:\Users
+ ASSERT_TRUE(GetProfilesDirectoryA(profiles_dir, &cch));
+
+ // On modern (English?) Windows, this is a directory symbolic link to
+ // C:\ProgramData. Symbolic links are rare on Windows and the user requires
+ // a special permission (by default granted to Administrative users) to
+ // create symbolic links.
+ EXPECT_FALSE(directory_exists(subdir(profiles_dir, "All Users")));
+
+ // On modern (English?) Windows, this is a directory junction to
+ // C:\Users\Default. Junctions are used throughout user profile directories
+ // for backwards compatibility and they don't require any special permissions
+ // to create.
+ EXPECT_FALSE(directory_exists(subdir(profiles_dir, "Default User")));
+}
+#endif
+
TEST(adb_utils, escape_arg) {
ASSERT_EQ(R"('')", escape_arg(""));
@@ -113,14 +123,20 @@
void test_mkdirs(const std::string basepath) {
// Test creating a directory hierarchy.
- EXPECT_TRUE(mkdirs(basepath));
+ ASSERT_TRUE(mkdirs(basepath));
// Test finding an existing directory hierarchy.
- EXPECT_TRUE(mkdirs(basepath));
+ ASSERT_TRUE(mkdirs(basepath));
+ // Test mkdirs on an existing hierarchy with a trailing slash.
+ ASSERT_TRUE(mkdirs(basepath + '/'));
+#if defined(_WIN32)
+ ASSERT_TRUE(mkdirs(basepath + '\\'));
+#endif
+
const std::string filepath = basepath + "/file";
// Verify that the hierarchy was created by trying to create a file in it.
- EXPECT_NE(-1, adb_creat(filepath.c_str(), 0600));
+ ASSERT_NE(-1, adb_creat(filepath.c_str(), 0600));
// If a file exists where we want a directory, the operation should fail.
- EXPECT_FALSE(mkdirs(filepath));
+ ASSERT_FALSE(mkdirs(filepath));
}
TEST(adb_utils, mkdirs) {
diff --git a/adb/bugreport.cpp b/adb/bugreport.cpp
index 64f01bd..9ed44a7 100644
--- a/adb/bugreport.cpp
+++ b/adb/bugreport.cpp
@@ -14,28 +14,40 @@
* limitations under the License.
*/
+#define TRACE_TAG ADB
+
+#include "bugreport.h"
+
#include <string>
+#include <vector>
#include <android-base/strings.h>
-#include "bugreport.h"
+#include "sysdeps.h"
+#include "adb_utils.h"
#include "file_sync_service.h"
-static constexpr char BUGZ_OK_PREFIX[] = "OK:";
-static constexpr char BUGZ_FAIL_PREFIX[] = "FAIL:";
+static constexpr char BUGZ_BEGIN_PREFIX[] = "BEGIN:";
static constexpr char BUGZ_PROGRESS_PREFIX[] = "PROGRESS:";
static constexpr char BUGZ_PROGRESS_SEPARATOR[] = "/";
+static constexpr char BUGZ_OK_PREFIX[] = "OK:";
+static constexpr char BUGZ_FAIL_PREFIX[] = "FAIL:";
// Custom callback used to handle the output of zipped bugreports.
class BugreportStandardStreamsCallback : public StandardStreamsCallbackInterface {
public:
- BugreportStandardStreamsCallback(const std::string& dest_file, bool show_progress, Bugreport* br)
+ BugreportStandardStreamsCallback(const std::string& dest_dir, const std::string& dest_file,
+ bool show_progress, Bugreport* br)
: br_(br),
+ src_file_(),
+ dest_dir_(dest_dir),
dest_file_(dest_file),
- line_message_(android::base::StringPrintf("generating %s", dest_file_.c_str())),
+ line_message_(),
+ invalid_lines_(),
show_progress_(show_progress),
- status_(-1),
+ status_(0),
line_() {
+ SetLineMessage();
}
void OnStdout(const char* buffer, int length) {
@@ -54,28 +66,72 @@
OnStream(nullptr, stderr, buffer, length);
}
int Done(int unused_) {
- // Process remaining line, if any...
+ // Process remaining line, if any.
ProcessLine(line_);
- // ..then return.
+
+ // Warn about invalid lines, if any.
+ if (!invalid_lines_.empty()) {
+ fprintf(stderr,
+ "WARNING: bugreportz generated %zu line(s) with unknown commands, "
+ "device might not support zipped bugreports:\n",
+ invalid_lines_.size());
+ for (const auto& line : invalid_lines_) {
+ fprintf(stderr, "\t%s\n", line.c_str());
+ }
+ fprintf(stderr,
+ "If the zipped bugreport was not generated, try 'adb bugreport' instead.\n");
+ }
+
+ // Pull the generated bug report.
+ if (status_ == 0) {
+ if (src_file_.empty()) {
+ fprintf(stderr, "bugreportz did not return a '%s' or '%s' line\n", BUGZ_OK_PREFIX,
+ BUGZ_FAIL_PREFIX);
+ return -1;
+ }
+ std::string destination;
+ if (dest_dir_.empty()) {
+ destination = dest_file_;
+ } else {
+ destination = android::base::StringPrintf("%s%c%s", dest_dir_.c_str(),
+ OS_PATH_SEPARATOR, dest_file_.c_str());
+ }
+ std::vector<const char*> srcs{src_file_.c_str()};
+ status_ =
+ br_->DoSyncPull(srcs, destination.c_str(), true, line_message_.c_str()) ? 0 : 1;
+ if (status_ != 0) {
+ fprintf(stderr,
+ "Bug report finished but could not be copied to '%s'.\n"
+ "Try to run 'adb pull %s <directory>'\n"
+ "to copy it to a directory that can be written.\n",
+ destination.c_str(), src_file_.c_str());
+ }
+ }
return status_;
}
private:
+ void SetLineMessage() {
+ line_message_ =
+ android::base::StringPrintf("generating %s", adb_basename(dest_file_).c_str());
+ }
+
+ void SetSrcFile(const std::string path) {
+ src_file_ = path;
+ if (!dest_dir_.empty()) {
+ // Only uses device-provided name when user passed a directory.
+ dest_file_ = adb_basename(path);
+ SetLineMessage();
+ }
+ }
+
void ProcessLine(const std::string& line) {
if (line.empty()) return;
- if (android::base::StartsWith(line, BUGZ_OK_PREFIX)) {
- if (show_progress_) {
- // Make sure pull message doesn't conflict with generation message.
- br_->UpdateProgress(line_message_, 100, 100);
- }
-
- const char* zip_file = &line[strlen(BUGZ_OK_PREFIX)];
- std::vector<const char*> srcs{zip_file};
- status_ = br_->DoSyncPull(srcs, dest_file_.c_str(), true, line_message_.c_str()) ? 0 : 1;
- if (status_ != 0) {
- fprintf(stderr, "Could not copy file '%s' to '%s'\n", zip_file, dest_file_.c_str());
- }
+ if (android::base::StartsWith(line, BUGZ_BEGIN_PREFIX)) {
+ SetSrcFile(&line[strlen(BUGZ_BEGIN_PREFIX)]);
+ } else if (android::base::StartsWith(line, BUGZ_OK_PREFIX)) {
+ SetSrcFile(&line[strlen(BUGZ_OK_PREFIX)]);
} else if (android::base::StartsWith(line, BUGZ_FAIL_PREFIX)) {
const char* error_message = &line[strlen(BUGZ_FAIL_PREFIX)];
fprintf(stderr, "Device failed to take a zipped bugreport: %s\n", error_message);
@@ -89,24 +145,38 @@
size_t idx2 = line.rfind(BUGZ_PROGRESS_SEPARATOR);
int progress = std::stoi(line.substr(idx1, (idx2 - idx1)));
int total = std::stoi(line.substr(idx2 + 1));
- br_->UpdateProgress(dest_file_, progress, total);
+ br_->UpdateProgress(line_message_, progress, total);
} else {
- fprintf(stderr,
- "WARNING: unexpected line (%s) returned by bugreportz, "
- "device probably does not support zipped bugreports.\n"
- "Try 'adb bugreport' instead.",
- line.c_str());
+ invalid_lines_.push_back(line);
}
}
Bugreport* br_;
- const std::string dest_file_;
- const std::string line_message_;
+
+ // Path of bugreport on device.
+ std::string src_file_;
+
+ // Bugreport destination on host, depending on argument passed on constructor:
+ // - if argument is a directory, dest_dir_ is set with it and dest_file_ will be the name
+ // of the bugreport reported by the device.
+ // - if argument is empty, dest_dir is set as the current directory and dest_file_ will be the
+ // name of the bugreport reported by the device.
+ // - otherwise, dest_dir_ is not set and dest_file_ is set with the value passed on constructor.
+ std::string dest_dir_, dest_file_;
+
+ // Message displayed on LinePrinter, it's updated every time the destination above change.
+ std::string line_message_;
+
+ // Lines sent by bugreportz that contain invalid commands; will be displayed at the end.
+ std::vector<std::string> invalid_lines_;
+
+ // Whether PROGRESS_LINES should be interpreted as progress.
bool show_progress_;
+
+ // Overall process of the operation, as returned by Done().
int status_;
- // Temporary buffer containing the characters read since the last newline
- // (\n).
+ // Temporary buffer containing the characters read since the last newline (\n).
std::string line_;
DISALLOW_COPY_AND_ASSIGN(BugreportStandardStreamsCallback);
@@ -116,34 +186,62 @@
int usage();
int Bugreport::DoIt(TransportType transport_type, const char* serial, int argc, const char** argv) {
- if (argc == 1) return SendShellCommand(transport_type, serial, "bugreport", false);
- if (argc != 2) return usage();
-
- // Zipped bugreport option - will call 'bugreportz', which prints the location
- // of the generated
- // file, then pull it to the destination file provided by the user.
- std::string dest_file = argv[1];
- if (!android::base::EndsWith(argv[1], ".zip")) {
- // TODO: use a case-insensitive comparison (like EndsWithIgnoreCase
- dest_file += ".zip";
- }
+ if (argc > 2) return usage();
// Gets bugreportz version.
- std::string bugz_stderr;
- DefaultStandardStreamsCallback version_callback(nullptr, &bugz_stderr);
+ std::string bugz_stdout, bugz_stderr;
+ DefaultStandardStreamsCallback version_callback(&bugz_stdout, &bugz_stderr);
int status = SendShellCommand(transport_type, serial, "bugreportz -v", false, &version_callback);
-
- if (status != 0) {
- fprintf(stderr,
- "Failed to get bugreportz version: 'bugreport -v' returned '%s' "
- "(code %d)."
- "\nIf the device does not support it, try running 'adb bugreport' "
- "to get a "
- "flat-file bugreport.",
- bugz_stderr.c_str(), status);
- return status;
- }
std::string bugz_version = android::base::Trim(bugz_stderr);
+ std::string bugz_output = android::base::Trim(bugz_stdout);
+
+ if (status != 0 || bugz_version.empty()) {
+ D("'bugreportz' -v results: status=%d, stdout='%s', stderr='%s'", status,
+ bugz_output.c_str(), bugz_version.c_str());
+ if (argc == 1) {
+ // Device does not support bugreportz: if called as 'adb bugreport', just falls out to
+ // the flat-file version.
+ fprintf(stderr,
+ "Failed to get bugreportz version, which is only available on devices "
+ "running Android 7.0 or later.\nTrying a plain-text bug report instead.\n");
+ return SendShellCommand(transport_type, serial, "bugreport", false);
+ }
+
+ // But if user explicitly asked for a zipped bug report, fails instead (otherwise calling
+ // 'bugreport' would generate a lot of output the user might not be prepared to handle).
+ fprintf(stderr,
+ "Failed to get bugreportz version: 'bugreportz -v' returned '%s' (code %d).\n"
+ "If the device does not run Android 7.0 or above, try 'adb bugreport' instead.\n",
+ bugz_output.c_str(), status);
+ return status != 0 ? status : -1;
+ }
+
+ std::string dest_file, dest_dir;
+
+ if (argc == 1) {
+ // No args - use current directory
+ if (!getcwd(&dest_dir)) {
+ perror("adb: getcwd failed");
+ return 1;
+ }
+ } else {
+ // Check whether argument is a directory or file
+ if (directory_exists(argv[1])) {
+ dest_dir = argv[1];
+ } else {
+ dest_file = argv[1];
+ }
+ }
+
+ if (dest_file.empty()) {
+ // Uses a default value until device provides the proper name
+ dest_file = "bugreport.zip";
+ } else {
+ if (!android::base::EndsWith(dest_file, ".zip")) {
+ // TODO: use a case-insensitive comparison (like EndsWithIgnoreCase
+ dest_file += ".zip";
+ }
+ }
bool show_progress = true;
std::string bugz_command = "bugreportz -p";
@@ -153,12 +251,11 @@
fprintf(stderr,
"Bugreport is in progress and it could take minutes to complete.\n"
"Please be patient and do not cancel or disconnect your device "
- "until it completes."
- "\n");
+ "until it completes.\n");
show_progress = false;
bugz_command = "bugreportz";
}
- BugreportStandardStreamsCallback bugz_callback(dest_file, show_progress, this);
+ BugreportStandardStreamsCallback bugz_callback(dest_dir, dest_file, show_progress, this);
return SendShellCommand(transport_type, serial, bugz_command, false, &bugz_callback);
}
diff --git a/adb/bugreport_test.cpp b/adb/bugreport_test.cpp
index a89d8dc..3cd2b6d 100644
--- a/adb/bugreport_test.cpp
+++ b/adb/bugreport_test.cpp
@@ -19,6 +19,12 @@
#include <gmock/gmock.h>
#include <gtest/gtest.h>
+#include <android-base/strings.h>
+#include <android-base/test_utils.h>
+
+#include "sysdeps.h"
+#include "adb_utils.h"
+
using ::testing::_;
using ::testing::Action;
using ::testing::ActionInterface;
@@ -30,7 +36,9 @@
using ::testing::StrEq;
using ::testing::WithArg;
using ::testing::internal::CaptureStderr;
+using ::testing::internal::CaptureStdout;
using ::testing::internal::GetCapturedStderr;
+using ::testing::internal::GetCapturedStdout;
// Empty function so tests don't need to be linked against file_sync_service.cpp, which requires
// SELinux and its transitive dependencies...
@@ -122,81 +130,139 @@
class BugreportTest : public ::testing::Test {
public:
- void SetBugreportzVersion(const std::string& version) {
+ void SetUp() {
+ if (!getcwd(&cwd_)) {
+ ADD_FAILURE() << "getcwd failed: " << strerror(errno);
+ return;
+ }
+ }
+
+ void ExpectBugreportzVersion(const std::string& version) {
EXPECT_CALL(br_,
SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -v", false, _))
.WillOnce(DoAll(WithArg<4>(WriteOnStderr(version.c_str())),
WithArg<4>(ReturnCallbackDone(0))));
}
- void ExpectProgress(int progress, int total) {
- EXPECT_CALL(br_, UpdateProgress(HasSubstr("file.zip"), progress, total));
+ void ExpectProgress(int progress, int total, const std::string& file = "file.zip") {
+ EXPECT_CALL(br_, UpdateProgress(StrEq("generating " + file), progress, total));
}
BugreportMock br_;
+ std::string cwd_; // TODO: make it static
};
-// Tests when called with invalid number of argumnts
+// Tests when called with invalid number of arguments
TEST_F(BugreportTest, InvalidNumberArgs) {
- const char* args[1024] = {"bugreport", "to", "principal"};
+ const char* args[] = {"bugreport", "to", "principal"};
ASSERT_EQ(-42, br_.DoIt(kTransportLocal, "HannibalLecter", 3, args));
}
-// Tests the legacy 'adb bugreport' option
-TEST_F(BugreportTest, FlatFileFormat) {
+// Tests the 'adb bugreport' option when the device does not support 'bugreportz' - it falls back
+// to the flat-file format ('bugreport' binary on device)
+TEST_F(BugreportTest, NoArgumentsPreNDevice) {
+ // clang-format off
+ EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -v", false, _))
+ .WillOnce(DoAll(WithArg<4>(WriteOnStderr("")),
+ // Write some bogus output on stdout to make sure it's ignored
+ WithArg<4>(WriteOnStdout("Dude, where is my bugreportz?")),
+ WithArg<4>(ReturnCallbackDone(0))));
+ // clang-format on
+ std::string bugreport = "Reported the bug was.";
+ CaptureStdout();
EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreport", false, _))
- .WillOnce(Return(0));
+ .WillOnce(DoAll(WithArg<4>(WriteOnStdout(bugreport)), Return(0)));
- const char* args[1024] = {"bugreport"};
+ const char* args[] = {"bugreport"};
+ ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 1, args));
+ ASSERT_THAT(GetCapturedStdout(), StrEq(bugreport));
+}
+
+// Tests the 'adb bugreport' option when the device supports 'bugreportz' version 1.0 - it will
+// save the bugreport in the current directory with the name provided by the device.
+TEST_F(BugreportTest, NoArgumentsNDevice) {
+ ExpectBugreportzVersion("1.0");
+
+ std::string dest_file =
+ android::base::StringPrintf("%s%cda_bugreport.zip", cwd_.c_str(), OS_PATH_SEPARATOR);
+ EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz", false, _))
+ .WillOnce(DoAll(WithArg<4>(WriteOnStdout("OK:/device/da_bugreport.zip")),
+ WithArg<4>(ReturnCallbackDone())));
+ EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/da_bugreport.zip")), StrEq(dest_file),
+ true, StrEq("generating da_bugreport.zip")))
+ .WillOnce(Return(true));
+
+ const char* args[] = {"bugreport"};
ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 1, args));
}
-// Tests 'adb bugreport file.zip' when it succeeds and device does not support
-// progress.
-TEST_F(BugreportTest, OkLegacy) {
- SetBugreportzVersion("1.0");
+// Tests the 'adb bugreport' option when the device supports 'bugreportz' version 1.1 - it will
+// save the bugreport in the current directory with the name provided by the device.
+TEST_F(BugreportTest, NoArgumentsPostNDevice) {
+ ExpectBugreportzVersion("1.1");
+ std::string dest_file =
+ android::base::StringPrintf("%s%cda_bugreport.zip", cwd_.c_str(), OS_PATH_SEPARATOR);
+ ExpectProgress(50, 100, "da_bugreport.zip");
+ EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
+ .WillOnce(DoAll(WithArg<4>(WriteOnStdout("BEGIN:/device/da_bugreport.zip\n")),
+ WithArg<4>(WriteOnStdout("PROGRESS:50/100\n")),
+ WithArg<4>(WriteOnStdout("OK:/device/da_bugreport.zip\n")),
+ WithArg<4>(ReturnCallbackDone())));
+ EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/da_bugreport.zip")), StrEq(dest_file),
+ true, StrEq("generating da_bugreport.zip")))
+ .WillOnce(Return(true));
+
+ const char* args[] = {"bugreport"};
+ ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 1, args));
+}
+
+// Tests 'adb bugreport file.zip' when it succeeds and device does not support progress.
+TEST_F(BugreportTest, OkNDevice) {
+ ExpectBugreportzVersion("1.0");
EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz", false, _))
.WillOnce(DoAll(WithArg<4>(WriteOnStdout("OK:/device/bugreport.zip")),
WithArg<4>(ReturnCallbackDone())));
EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/bugreport.zip")), StrEq("file.zip"),
- true, HasSubstr("file.zip")))
+ true, StrEq("generating file.zip")))
.WillOnce(Return(true));
- const char* args[1024] = {"bugreport", "file.zip"};
+ const char* args[] = {"bugreport", "file.zip"};
ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
}
// Tests 'adb bugreport file.zip' when it succeeds but response was sent in
// multiple buffer writers and without progress updates.
-TEST_F(BugreportTest, OkLegacySplitBuffer) {
- SetBugreportzVersion("1.0");
+TEST_F(BugreportTest, OkNDeviceSplitBuffer) {
+ ExpectBugreportzVersion("1.0");
EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz", false, _))
.WillOnce(DoAll(WithArg<4>(WriteOnStdout("OK:/device")),
WithArg<4>(WriteOnStdout("/bugreport.zip")),
WithArg<4>(ReturnCallbackDone())));
EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/bugreport.zip")), StrEq("file.zip"),
- true, HasSubstr("file.zip")))
+ true, StrEq("generating file.zip")))
.WillOnce(Return(true));
- const char* args[1024] = {"bugreport", "file.zip"};
+ const char* args[] = {"bugreport", "file.zip"};
ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
}
// Tests 'adb bugreport file.zip' when it succeeds and displays progress.
-TEST_F(BugreportTest, Ok) {
- SetBugreportzVersion("1.1");
+TEST_F(BugreportTest, OkProgress) {
+ ExpectBugreportzVersion("1.1");
ExpectProgress(1, 100);
ExpectProgress(10, 100);
ExpectProgress(50, 100);
ExpectProgress(99, 100);
- ExpectProgress(100, 100);
// clang-format off
EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
+ // NOTE: DoAll accepts at most 10 arguments, and we're almost reached that limit...
.WillOnce(DoAll(
+ // Name might change on OK, so make sure the right one is picked.
+ WithArg<4>(WriteOnStdout("BEGIN:/device/bugreport___NOT.zip\n")),
// Progress line in one write
WithArg<4>(WriteOnStdout("PROGRESS:1/100\n")),
// Add some bogus lines
- WithArg<4>(WriteOnStdout("\nDUDE:SWEET\n\n")),
+ WithArg<4>(WriteOnStdout("\nDUDE:SWEET\n\nBLA\n\nBLA\nBLA\n\n")),
// Multiple progress lines in one write
WithArg<4>(WriteOnStdout("PROGRESS:10/100\nPROGRESS:50/100\n")),
// Progress line in multiple writes
@@ -207,38 +273,76 @@
WithArg<4>(WriteOnStdout("OK:/device/bugreport")),
WithArg<4>(WriteOnStdout(".zip")),
WithArg<4>(ReturnCallbackDone())));
+ // clang-format on
EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/bugreport.zip")), StrEq("file.zip"),
- true, HasSubstr("file.zip")))
+ true, StrEq("generating file.zip")))
.WillOnce(Return(true));
- const char* args[1024] = {"bugreport", "file.zip"};
+ const char* args[] = {"bugreport", "file.zip"};
+ ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+}
+
+// Tests 'adb bugreport dir' when it succeeds and destination is a directory.
+TEST_F(BugreportTest, OkDirectory) {
+ ExpectBugreportzVersion("1.1");
+ TemporaryDir td;
+ std::string dest_file =
+ android::base::StringPrintf("%s%cda_bugreport.zip", td.path, OS_PATH_SEPARATOR);
+
+ EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
+ .WillOnce(DoAll(WithArg<4>(WriteOnStdout("BEGIN:/device/da_bugreport.zip\n")),
+ WithArg<4>(WriteOnStdout("OK:/device/da_bugreport.zip")),
+ WithArg<4>(ReturnCallbackDone())));
+ EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/da_bugreport.zip")), StrEq(dest_file),
+ true, StrEq("generating da_bugreport.zip")))
+ .WillOnce(Return(true));
+
+ const char* args[] = {"bugreport", td.path};
ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
}
// Tests 'adb bugreport file' when it succeeds
TEST_F(BugreportTest, OkNoExtension) {
- SetBugreportzVersion("1.1");
- ExpectProgress(100, 100);
+ ExpectBugreportzVersion("1.1");
EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
.WillOnce(DoAll(WithArg<4>(WriteOnStdout("OK:/device/bugreport.zip\n")),
WithArg<4>(ReturnCallbackDone())));
EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/bugreport.zip")), StrEq("file.zip"),
- true, HasSubstr("file.zip")))
+ true, StrEq("generating file.zip")))
.WillOnce(Return(true));
- const char* args[1024] = {"bugreport", "file"};
+ const char* args[] = {"bugreport", "file"};
+ ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+}
+
+// Tests 'adb bugreport dir' when it succeeds and destination is a directory and device runs N.
+TEST_F(BugreportTest, OkNDeviceDirectory) {
+ ExpectBugreportzVersion("1.0");
+ TemporaryDir td;
+ std::string dest_file =
+ android::base::StringPrintf("%s%cda_bugreport.zip", td.path, OS_PATH_SEPARATOR);
+
+ EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz", false, _))
+ .WillOnce(DoAll(WithArg<4>(WriteOnStdout("BEGIN:/device/da_bugreport.zip\n")),
+ WithArg<4>(WriteOnStdout("OK:/device/da_bugreport.zip")),
+ WithArg<4>(ReturnCallbackDone())));
+ EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/da_bugreport.zip")), StrEq(dest_file),
+ true, StrEq("generating da_bugreport.zip")))
+ .WillOnce(Return(true));
+
+ const char* args[] = {"bugreport", td.path};
ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
}
// Tests 'adb bugreport file.zip' when the bugreport itself failed
TEST_F(BugreportTest, BugreportzReturnedFail) {
- SetBugreportzVersion("1.1");
+ ExpectBugreportzVersion("1.1");
EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
.WillOnce(
DoAll(WithArg<4>(WriteOnStdout("FAIL:D'OH!\n")), WithArg<4>(ReturnCallbackDone())));
CaptureStderr();
- const char* args[1024] = {"bugreport", "file.zip"};
+ const char* args[] = {"bugreport", "file.zip"};
ASSERT_EQ(-1, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
ASSERT_THAT(GetCapturedStderr(), HasSubstr("D'OH!"));
}
@@ -247,13 +351,13 @@
// was sent in
// multiple buffer writes
TEST_F(BugreportTest, BugreportzReturnedFailSplitBuffer) {
- SetBugreportzVersion("1.1");
+ ExpectBugreportzVersion("1.1");
EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
.WillOnce(DoAll(WithArg<4>(WriteOnStdout("FAIL")), WithArg<4>(WriteOnStdout(":D'OH!\n")),
WithArg<4>(ReturnCallbackDone())));
CaptureStderr();
- const char* args[1024] = {"bugreport", "file.zip"};
+ const char* args[] = {"bugreport", "file.zip"};
ASSERT_EQ(-1, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
ASSERT_THAT(GetCapturedStderr(), HasSubstr("D'OH!"));
}
@@ -261,13 +365,13 @@
// Tests 'adb bugreport file.zip' when the bugreportz returned an unsupported
// response.
TEST_F(BugreportTest, BugreportzReturnedUnsupported) {
- SetBugreportzVersion("1.1");
+ ExpectBugreportzVersion("1.1");
EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
.WillOnce(DoAll(WithArg<4>(WriteOnStdout("bugreportz? What am I, a zombie?")),
WithArg<4>(ReturnCallbackDone())));
CaptureStderr();
- const char* args[1024] = {"bugreport", "file.zip"};
+ const char* args[] = {"bugreport", "file.zip"};
ASSERT_EQ(-1, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
ASSERT_THAT(GetCapturedStderr(), HasSubstr("bugreportz? What am I, a zombie?"));
}
@@ -277,24 +381,31 @@
EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -v", false, _))
.WillOnce(Return(666));
- const char* args[1024] = {"bugreport", "file.zip"};
+ const char* args[] = {"bugreport", "file.zip"};
ASSERT_EQ(666, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
}
+// Tests 'adb bugreport file.zip' when the bugreportz -v returns status 0 but with no output.
+TEST_F(BugreportTest, BugreportzVersionEmpty) {
+ ExpectBugreportzVersion("");
+
+ const char* args[] = {"bugreport", "file.zip"};
+ ASSERT_EQ(-1, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+}
+
// Tests 'adb bugreport file.zip' when the main bugreportz command failed
TEST_F(BugreportTest, BugreportzFailed) {
- SetBugreportzVersion("1.1");
+ ExpectBugreportzVersion("1.1");
EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
.WillOnce(Return(666));
- const char* args[1024] = {"bugreport", "file.zip"};
+ const char* args[] = {"bugreport", "file.zip"};
ASSERT_EQ(666, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
}
// Tests 'adb bugreport file.zip' when the bugreport could not be pulled
TEST_F(BugreportTest, PullFails) {
- SetBugreportzVersion("1.1");
- ExpectProgress(100, 100);
+ ExpectBugreportzVersion("1.1");
EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
.WillOnce(DoAll(WithArg<4>(WriteOnStdout("OK:/device/bugreport.zip")),
WithArg<4>(ReturnCallbackDone())));
@@ -302,6 +413,6 @@
true, HasSubstr("file.zip")))
.WillOnce(Return(false));
- const char* args[1024] = {"bugreport", "file.zip"};
+ const char* args[] = {"bugreport", "file.zip"};
ASSERT_EQ(1, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
}
diff --git a/adb/commandline.cpp b/adb/commandline.cpp
index 0685b70..23827de 100644
--- a/adb/commandline.cpp
+++ b/adb/commandline.cpp
@@ -81,6 +81,7 @@
static void help() {
fprintf(stderr, "%s\n", adb_version().c_str());
+ // clang-format off
fprintf(stderr,
" -a - directs adb to listen on all interfaces for a connection\n"
" -d - directs command to the only connected USB device\n"
@@ -173,9 +174,11 @@
" (-g: grant all runtime permissions)\n"
" adb uninstall [-k] <package> - remove this app package from the device\n"
" ('-k' means keep the data and cache directories)\n"
- " adb bugreport [<zip_file>] - return all information from the device\n"
- " that should be included in a bug report.\n"
- "\n"
+ " adb bugreport [<path>] - return all information from the device that should be included in a zipped bug report.\n"
+ " If <path> is a file, the bug report will be saved as that file.\n"
+ " If <path> is a directory, the bug report will be saved in that directory with the name provided by the device.\n"
+ " If <path> is omitted, the bug report will be saved in the current directory with the name provided by the device.\n"
+ " NOTE: if the device does not support zipped bug reports, the bug report will be output on stdout.\n"
" adb backup [-f <file>] [-apk|-noapk] [-obb|-noobb] [-shared|-noshared] [-all] [-system|-nosystem] [<packages...>]\n"
" - write an archive of the device's data to <file>.\n"
" If no -f option is supplied then the data is written\n"
@@ -249,8 +252,8 @@
" ADB_TRACE - Print debug information. A comma separated list of the following values\n"
" 1 or all, adb, sockets, packets, rwx, usb, sync, sysdeps, transport, jdwp\n"
" ANDROID_SERIAL - The serial number to connect to. -s takes priority over this if given.\n"
- " ANDROID_LOG_TAGS - When used with the logcat option, only these debug tags are printed.\n"
- );
+ " ANDROID_LOG_TAGS - When used with the logcat option, only these debug tags are printed.\n");
+ // clang-format on
}
int usage() {
@@ -1327,7 +1330,7 @@
if (hint.find_first_of(OS_PATH_SEPARATORS) != std::string::npos) {
std::string cwd;
if (!getcwd(&cwd)) {
- fprintf(stderr, "adb: getcwd failed: %s\n", strerror(errno));
+ perror("adb: getcwd failed");
return "";
}
return android::base::StringPrintf("%s%c%s", cwd.c_str(), OS_PATH_SEPARATOR, hint.c_str());
diff --git a/adb/sysdeps.h b/adb/sysdeps.h
index 75dcc86..bd19f88 100644
--- a/adb/sysdeps.h
+++ b/adb/sysdeps.h
@@ -34,6 +34,8 @@
#include <android-base/unique_fd.h>
#include <android-base/utf8.h>
+#include "sysdeps/stat.h"
+
/*
* TEMP_FAILURE_RETRY is defined by some, but not all, versions of
* <unistd.h>. (Alas, it is not as standard as we'd hoped!) So, if it's
@@ -199,8 +201,6 @@
/* nothing really */
}
-#define lstat stat /* no symlinks on Win32 */
-
#define S_ISLNK(m) 0 /* no symlinks on Win32 */
extern int adb_unlink(const char* path);
@@ -307,27 +307,6 @@
return isalpha(path[0]) && path[1] == ':' && path[2] == '\\';
}
-// We later define a macro mapping 'stat' to 'adb_stat'. This causes:
-// struct stat s;
-// stat(filename, &s);
-// To turn into the following:
-// struct adb_stat s;
-// adb_stat(filename, &s);
-// To get this to work, we need to make 'struct adb_stat' the same as
-// 'struct stat'. Note that this definition of 'struct adb_stat' uses the
-// *current* macro definition of stat, so it may actually be inheriting from
-// struct _stat32i64 (or some other remapping).
-struct adb_stat : public stat {};
-
-static_assert(sizeof(struct adb_stat) == sizeof(struct stat),
- "structures should be the same");
-
-extern int adb_stat(const char* f, struct adb_stat* s);
-
-// stat is already a macro, undefine it so we can redefine it.
-#undef stat
-#define stat adb_stat
-
// UTF-8 versions of POSIX APIs.
extern DIR* adb_opendir(const char* dirname);
extern struct dirent* adb_readdir(DIR* dir);
diff --git a/adb/sysdeps/stat.h b/adb/sysdeps/stat.h
new file mode 100644
index 0000000..5953595
--- /dev/null
+++ b/adb/sysdeps/stat.h
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#if defined(_WIN32)
+// stat is broken on Win32: stat on a path with a trailing slash or backslash will always fail with
+// ENOENT.
+int adb_stat(const char* path, struct adb_stat* buf);
+
+// We later define a macro mapping 'stat' to 'adb_stat'. This causes:
+// struct stat s;
+// stat(filename, &s);
+// To turn into the following:
+// struct adb_stat s;
+// adb_stat(filename, &s);
+// To get this to work, we need to make 'struct adb_stat' the same as
+// 'struct stat'. Note that this definition of 'struct adb_stat' uses the
+// *current* macro definition of stat, so it may actually be inheriting from
+// struct _stat32i64 (or some other remapping).
+struct adb_stat : public stat {};
+
+#undef stat
+#define stat adb_stat
+
+// Windows doesn't have lstat.
+#define lstat adb_stat
+
+#endif
diff --git a/adb/sysdeps/stat_test.cpp b/adb/sysdeps/stat_test.cpp
new file mode 100644
index 0000000..2c2e0ee
--- /dev/null
+++ b/adb/sysdeps/stat_test.cpp
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <string>
+
+#include <android-base/test_utils.h>
+#include <gtest/gtest.h>
+
+#include "adb_utils.h"
+#include "sysdeps.h"
+
+TEST(sysdeps, stat) {
+ TemporaryDir td;
+ TemporaryFile tf;
+
+ struct stat st;
+ ASSERT_EQ(0, stat(td.path, &st));
+ ASSERT_FALSE(S_ISREG(st.st_mode));
+ ASSERT_TRUE(S_ISDIR(st.st_mode));
+
+ ASSERT_EQ(0, stat((std::string(td.path) + '/').c_str(), &st));
+ ASSERT_TRUE(S_ISDIR(st.st_mode));
+
+#if defined(_WIN32)
+ ASSERT_EQ(0, stat((std::string(td.path) + '\\').c_str(), &st));
+ ASSERT_TRUE(S_ISDIR(st.st_mode));
+#endif
+
+ std::string nonexistent_path = std::string(td.path) + "/nonexistent";
+ ASSERT_EQ(-1, stat(nonexistent_path.c_str(), &st));
+ ASSERT_EQ(ENOENT, errno);
+
+ ASSERT_EQ(-1, stat((nonexistent_path + "/").c_str(), &st));
+ ASSERT_EQ(ENOENT, errno);
+
+#if defined(_WIN32)
+ ASSERT_EQ(-1, stat((nonexistent_path + "\\").c_str(), &st));
+ ASSERT_EQ(ENOENT, errno);
+#endif
+
+ ASSERT_EQ(0, stat(tf.path, &st));
+ ASSERT_TRUE(S_ISREG(st.st_mode));
+ ASSERT_FALSE(S_ISDIR(st.st_mode));
+
+ ASSERT_EQ(-1, stat((std::string(tf.path) + '/').c_str(), &st));
+ ASSERT_EQ(ENOTDIR, errno);
+
+#if defined(_WIN32)
+ ASSERT_EQ(-1, stat((std::string(tf.path) + '\\').c_str(), &st));
+ ASSERT_EQ(ENOTDIR, errno);
+#endif
+}
diff --git a/adb/sysdeps/win32/stat.cpp b/adb/sysdeps/win32/stat.cpp
new file mode 100644
index 0000000..844c1ce
--- /dev/null
+++ b/adb/sysdeps/win32/stat.cpp
@@ -0,0 +1,66 @@
+/*
+ * 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 "sysdeps/stat.h"
+
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <string>
+
+#include <android-base/utf8.h>
+
+// Version of stat() that takes a UTF-8 path.
+int adb_stat(const char* path, struct adb_stat* s) {
+// This definition of wstat seems to be missing from <sys/stat.h>.
+#if defined(_FILE_OFFSET_BITS) && (_FILE_OFFSET_BITS == 64)
+#ifdef _USE_32BIT_TIME_T
+#define wstat _wstat32i64
+#else
+#define wstat _wstat64
+#endif
+#else
+// <sys/stat.h> has a function prototype for wstat() that should be available.
+#endif
+
+ std::wstring path_wide;
+ if (!android::base::UTF8ToWide(path, &path_wide)) {
+ errno = ENOENT;
+ return -1;
+ }
+
+ // If the path has a trailing slash, stat will fail with ENOENT regardless of whether the path
+ // is a directory or not.
+ bool expected_directory = false;
+ while (*path_wide.rbegin() == u'/' || *path_wide.rbegin() == u'\\') {
+ path_wide.pop_back();
+ expected_directory = true;
+ }
+
+ struct adb_stat st;
+ int result = wstat(path_wide.c_str(), &st);
+ if (result == 0 && expected_directory) {
+ if (!S_ISDIR(st.st_mode)) {
+ errno = ENOTDIR;
+ return -1;
+ }
+ }
+
+ memcpy(s, &st, sizeof(st));
+ return result;
+}
diff --git a/adb/sysdeps_win32.cpp b/adb/sysdeps_win32.cpp
index bc09fdc..e641680 100644
--- a/adb/sysdeps_win32.cpp
+++ b/adb/sysdeps_win32.cpp
@@ -2298,30 +2298,6 @@
}
}
-// Version of stat() that takes a UTF-8 path.
-int adb_stat(const char* path, struct adb_stat* s) {
-#pragma push_macro("wstat")
-// This definition of wstat seems to be missing from <sys/stat.h>.
-#if defined(_FILE_OFFSET_BITS) && (_FILE_OFFSET_BITS == 64)
-#ifdef _USE_32BIT_TIME_T
-#define wstat _wstat32i64
-#else
-#define wstat _wstat64
-#endif
-#else
-// <sys/stat.h> has a function prototype for wstat() that should be available.
-#endif
-
- std::wstring path_wide;
- if (!android::base::UTF8ToWide(path, &path_wide)) {
- return -1;
- }
-
- return wstat(path_wide.c_str(), s);
-
-#pragma pop_macro("wstat")
-}
-
// Version of opendir() that takes a UTF-8 path.
DIR* adb_opendir(const char* path) {
std::wstring path_wide;
diff --git a/bootstat/boot_event_record_store.cpp b/bootstat/boot_event_record_store.cpp
index ef4f68e..346eada 100644
--- a/bootstat/boot_event_record_store.cpp
+++ b/bootstat/boot_event_record_store.cpp
@@ -25,6 +25,7 @@
#include <utility>
#include <android-base/file.h>
#include <android-base/logging.h>
+#include <android-base/parseint.h>
#include "histogram_logger.h"
#include "uptime_parser.h"
@@ -57,8 +58,10 @@
// Ignore existing bootstat records (which do not contain file content).
if (!content.empty()) {
- int32_t value = std::stoi(content);
- bootstat::LogHistogram("bootstat_mtime_matches_content", value == *uptime);
+ int32_t value;
+ if (android::base::ParseInt(content.c_str(), &value)) {
+ bootstat::LogHistogram("bootstat_mtime_matches_content", value == *uptime);
+ }
}
return true;
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index 08fc5af..26e0ffc 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -28,6 +28,7 @@
#include <memory>
#include <string>
#include <android-base/logging.h>
+#include <android-base/parseint.h>
#include <cutils/properties.h>
#include <log/log.h>
#include "boot_event_record_store.h"
@@ -147,7 +148,10 @@
std::string boot_complete_prefix = "boot_complete";
std::string build_date_str = GetProperty("ro.build.date.utc");
- int32_t build_date = std::stoi(build_date_str);
+ int32_t build_date;
+ if (!android::base::ParseInt(build_date_str.c_str(), &build_date)) {
+ return std::string();
+ }
BootEventRecordStore boot_event_store;
BootEventRecordStore::BootEventRecord record;
@@ -171,6 +175,10 @@
// ota_boot_complete. The latter signifies that the device is booting after
// a system update.
std::string boot_complete_prefix = CalculateBootCompletePrefix();
+ if (boot_complete_prefix.empty()) {
+ // The system is hosed because the build date property could not be read.
+ return;
+ }
// post_decrypt_time_elapsed is only logged on encrypted devices.
if (boot_event_store.GetBootEvent("post_decrypt_time_elapsed", &record)) {
diff --git a/logcat/logcatd.rc b/logcat/logcatd.rc
index ce1a451..4d5c80c 100644
--- a/logcat/logcatd.rc
+++ b/logcat/logcatd.rc
@@ -35,7 +35,8 @@
# 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)
- 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}
+ # 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}
start logcatd
# stop logcatd service and clear data
diff --git a/logd/LogKlog.cpp b/logd/LogKlog.cpp
index a0b7499..da0aecc 100644
--- a/logd/LogKlog.cpp
+++ b/logd/LogKlog.cpp
@@ -630,126 +630,124 @@
const char *tag = "";
const char *etag = tag;
size_t taglen = len - (p - buf);
- if (!isspace(*p) && *p) {
- const char *bt, *et, *cp;
+ const char *bt = p;
- bt = p;
- if ((taglen >= 6) && !fast<strncmp>(p, "[INFO]", 6)) {
- // <PRI>[<TIME>] "[INFO]"<tag> ":" message
- bt = p + 6;
- taglen -= 6;
- }
- for(et = bt; taglen && *et && (*et != ':') && !isspace(*et); ++et, --taglen) {
- // skip ':' within [ ... ]
- if (*et == '[') {
- while (taglen && *et && *et != ']') {
- ++et;
- --taglen;
- }
- }
- }
- for(cp = et; taglen && isspace(*cp); ++cp, --taglen);
- size_t size;
+ static const char infoBrace[] = "[INFO]";
+ static const size_t infoBraceLen = strlen(infoBrace);
+ if ((taglen >= infoBraceLen) && !fast<strncmp>(p, infoBrace, infoBraceLen)) {
+ // <PRI>[<TIME>] "[INFO]"<tag> ":" message
+ bt = p + infoBraceLen;
+ taglen -= infoBraceLen;
+ }
+ const char *et;
+ for (et = bt; taglen && *et && (*et != ':') && !isspace(*et); ++et, --taglen) {
+ // skip ':' within [ ... ]
+ if (*et == '[') {
+ while (taglen && *et && *et != ']') {
+ ++et;
+ --taglen;
+ }
+ if (!taglen) {
+ break;
+ }
+ }
+ }
+ const char *cp;
+ for (cp = et; taglen && isspace(*cp); ++cp, --taglen);
+
+ // Validate tag
+ size_t size = et - bt;
+ if (taglen && size) {
if (*cp == ':') {
+ // ToDo: handle case insensitive colon separated logging stutter:
+ // <tag> : <tag>: ...
+
// One Word
tag = bt;
etag = et;
p = cp + 1;
- } else if (taglen) {
- size = et - bt;
- if ((taglen > size) && // enough space for match plus trailing :
- (*bt == *cp) && // ubber fast<strncmp> pair
- fast<strncmp>(bt + 1, cp + 1, size - 1)) {
- // <PRI>[<TIME>] <tag>_host '<tag>.<num>' : message
- if (!fast<strncmp>(bt + size - 5, "_host", 5)
- && !fast<strncmp>(bt + 1, cp + 1, size - 6)) {
+ } else if ((taglen > size) && (tolower(*bt) == tolower(*cp))) {
+ // clean up any tag stutter
+ if (!fast<strncasecmp>(bt + 1, cp + 1, size - 1)) { // no match
+ // <PRI>[<TIME>] <tag> <tag> : message
+ // <PRI>[<TIME>] <tag> <tag>: message
+ // <PRI>[<TIME>] <tag> '<tag>.<num>' : message
+ // <PRI>[<TIME>] <tag> '<tag><num>' : message
+ // <PRI>[<TIME>] <tag> '<tag><stuff>' : message
+ const char *b = cp;
+ cp += size;
+ taglen -= size;
+ while (--taglen && !isspace(*++cp) && (*cp != ':'));
+ const char *e;
+ for (e = cp; taglen && isspace(*cp); ++cp, --taglen);
+ if (taglen && (*cp == ':')) {
+ tag = b;
+ etag = e;
+ p = cp + 1;
+ }
+ } else {
+ // what about <PRI>[<TIME>] <tag>_host '<tag><stuff>' : message
+ static const char host[] = "_host";
+ static const size_t hostlen = strlen(host);
+ if ((size > hostlen) &&
+ !fast<strncmp>(bt + size - hostlen, host, hostlen) &&
+ !fast<strncmp>(bt + 1, cp + 1, size - hostlen - 1)) {
const char *b = cp;
- cp += size - 5;
- taglen -= size - 5;
+ cp += size - hostlen;
+ taglen -= size - hostlen;
if (*cp == '.') {
while (--taglen && !isspace(*++cp) && (*cp != ':'));
const char *e;
- for(e = cp; taglen && isspace(*cp); ++cp, --taglen);
- if (*cp == ':') {
+ for (e = cp; taglen && isspace(*cp); ++cp, --taglen);
+ if (taglen && (*cp == ':')) {
tag = b;
etag = e;
p = cp + 1;
}
}
} else {
- while (--taglen && !isspace(*++cp) && (*cp != ':'));
- const char *e;
- for(e = cp; taglen && isspace(*cp); ++cp, --taglen);
- // Two words
- if (*cp == ':') {
- tag = bt;
- etag = e;
- p = cp + 1;
- }
- }
- } else if (isspace(cp[size])) {
- cp += size;
- taglen -= size;
- while (--taglen && isspace(*++cp));
- // <PRI>[<TIME>] <tag> <tag> : message
- if (*cp == ':') {
- tag = bt;
- etag = et;
- p = cp + 1;
- }
- } else if (cp[size] == ':') {
- // <PRI>[<TIME>] <tag> <tag> : message
- tag = bt;
- etag = et;
- p = cp + size + 1;
- } else if ((cp[size] == '.') || isdigit(cp[size])) {
- // <PRI>[<TIME>] <tag> '<tag>.<num>' : message
- // <PRI>[<TIME>] <tag> '<tag><num>' : message
- const char *b = cp;
- cp += size;
- taglen -= size;
- while (--taglen && !isspace(*++cp) && (*cp != ':'));
- const char *e = cp;
- while (taglen && isspace(*cp)) {
- ++cp;
- --taglen;
- }
- if (*cp == ':') {
- tag = b;
- etag = e;
- p = cp + 1;
- }
- } else {
- while (--taglen && !isspace(*++cp) && (*cp != ':'));
- const char *e = cp;
- while (taglen && isspace(*cp)) {
- ++cp;
- --taglen;
- }
- // Two words
- if (*cp == ':') {
- tag = bt;
- etag = e;
- p = cp + 1;
+ goto twoWord;
}
}
- } /* else no tag */
- size = etag - tag;
- if ((size <= 1)
- // register names like x9
- || ((size == 2) && (isdigit(tag[0]) || isdigit(tag[1])))
- // register names like x18 but not driver names like en0
- || ((size == 3) && (isdigit(tag[1]) && isdigit(tag[2])))
- // blacklist
- || ((size == 3) && !fast<strncmp>(tag, "CPU", 3))
- || ((size == 7) && !fast<strncasecmp>(tag, "WARNING", 7))
- || ((size == 5) && !fast<strncasecmp>(tag, "ERROR", 5))
- || ((size == 4) && !fast<strncasecmp>(tag, "INFO", 4))) {
- p = start;
- etag = tag = "";
+ } else {
+ // <PRI>[<TIME>] <tag> <stuff>' : message
+twoWord: while (--taglen && !isspace(*++cp) && (*cp != ':'));
+ const char *e;
+ for (e = cp; taglen && isspace(*cp); ++cp, --taglen);
+ // Two words
+ if (taglen && (*cp == ':')) {
+ tag = bt;
+ etag = e;
+ p = cp + 1;
+ }
}
+ } // else no tag
+
+ static const char cpu[] = "CPU";
+ static const size_t cpuLen = strlen(cpu);
+ static const char warning[] = "WARNING";
+ static const size_t warningLen = strlen(warning);
+ static const char error[] = "ERROR";
+ static const size_t errorLen = strlen(error);
+ static const char info[] = "INFO";
+ static const size_t infoLen = strlen(info);
+
+ size = etag - tag;
+ if ((size <= 1)
+ // register names like x9
+ || ((size == 2) && (isdigit(tag[0]) || isdigit(tag[1])))
+ // register names like x18 but not driver names like en0
+ || ((size == 3) && (isdigit(tag[1]) && isdigit(tag[2])))
+ // blacklist
+ || ((size == cpuLen) && !fast<strncmp>(tag, cpu, cpuLen))
+ || ((size == warningLen) && !fast<strncasecmp>(tag, warning, warningLen))
+ || ((size == errorLen) && !fast<strncasecmp>(tag, error, errorLen))
+ || ((size == infoLen) && !fast<strncasecmp>(tag, info, infoLen))) {
+ p = start;
+ etag = tag = "";
}
+
// Suppress additional stutter in tag:
// eg: [143:healthd]healthd -> [143:healthd]
taglen = etag - tag;