Moved some functions to DumpstateUtil.h.
dumpstate_board() is been refactored into a HIDL interface, and the HIDL
implementations will need help functions to dump files and run commands into
a file descriptor.
BUG: 31982882
Test: dumpstate_test passes
Test: manual verification
Change-Id: I7a32f0ac236dae34fd85abe47bed0e52a34c5f36
diff --git a/cmds/dumpstate/Android.mk b/cmds/dumpstate/Android.mk
index e11bf30..5b0ced9 100644
--- a/cmds/dumpstate/Android.mk
+++ b/cmds/dumpstate/Android.mk
@@ -22,6 +22,27 @@
liblog \
libselinux \
libutils
+COMMON_STATIC_LIBRARIES := \
+ libdumpstateutil \
+ $(COMMON_ZIP_LIBRARIES)
+
+# ====================#
+# libdumpstateutil #
+# ====================#
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libdumpstateutil
+
+LOCAL_CFLAGS := $(COMMON_LOCAL_CFLAGS)
+LOCAL_C_INCLUDES := $(LOCAL_PATH)
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)
+LOCAL_SRC_FILES := \
+ utils.cpp # TODO: temporary, until functions are moved to DumpstateUtil.cpp
+# TODO: include just what it uses once split from utils.cpp
+LOCAL_SHARED_LIBRARIES := $(COMMON_SHARED_LIBRARIES)
+LOCAL_STATIC_LIBRARIES := $(COMMON_ZIP_LIBRARIES)
+
+include $(BUILD_STATIC_LIBRARY)
# ====================#
# libdumpstateheaders #
@@ -35,7 +56,7 @@
LOCAL_EXPORT_SHARED_LIBRARY_HEADERS := \
$(COMMON_SHARED_LIBRARIES)
LOCAL_EXPORT_STATIC_LIBRARY_HEADERS := \
- $(COMMON_ZIP_LIBRARIES)
+ $(COMMON_STATIC_LIBRARIES)
# Soong requires that whats is on LOCAL_EXPORTED_ is also on LOCAL_
LOCAL_SHARED_LIBRARIES := $(LOCAL_EXPORT_SHARED_LIBRARY_HEADERS)
LOCAL_STATIC_LIBRARIES := $(LOCAL_EXPORT_STATIC_LIBRARY_HEADERS)
@@ -81,7 +102,7 @@
LOCAL_SHARED_LIBRARIES := $(COMMON_SHARED_LIBRARIES)
-LOCAL_STATIC_LIBRARIES := $(COMMON_ZIP_LIBRARIES)
+LOCAL_STATIC_LIBRARIES := $(COMMON_STATIC_LIBRARIES)
LOCAL_HAL_STATIC_LIBRARIES := libdumpstate
@@ -106,7 +127,7 @@
DumpstateService.cpp \
tests/dumpstate_test.cpp
-LOCAL_STATIC_LIBRARIES := $(COMMON_ZIP_LIBRARIES) \
+LOCAL_STATIC_LIBRARIES := $(COMMON_STATIC_LIBRARIES) \
libgmock
LOCAL_SHARED_LIBRARIES := $(COMMON_SHARED_LIBRARIES)
diff --git a/cmds/dumpstate/DumpstateUtil.h b/cmds/dumpstate/DumpstateUtil.h
new file mode 100644
index 0000000..9c60f0d
--- /dev/null
+++ b/cmds/dumpstate/DumpstateUtil.h
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2008 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 FRAMEWORK_NATIVE_CMD_DUMPSTATE_UTIL_H_
+#define FRAMEWORK_NATIVE_CMD_DUMPSTATE_UTIL_H_
+
+/*
+ * Defines the Linux user that should be executing a command.
+ */
+enum RootMode {
+ /* Explicitly change the `uid` and `gid` to be `shell`.*/
+ DROP_ROOT,
+ /* Don't change the `uid` and `gid`. */
+ DONT_DROP_ROOT,
+ /* Prefix the command with `/PATH/TO/su root`. Won't work non user builds. */
+ SU_ROOT
+};
+
+/*
+ * Defines what should happen with the `stdout` stream of a command.
+ */
+enum StdoutMode {
+ /* Don't change `stdout`. */
+ NORMAL_STDOUT,
+ /* Redirect `stdout` to `stderr`. */
+ REDIRECT_TO_STDERR
+};
+
+/*
+ * Value object used to set command options.
+ *
+ * Typically constructed using a builder with chained setters. Examples:
+ *
+ * CommandOptions::WithTimeout(20).AsRoot().Build();
+ * CommandOptions::WithTimeout(10).Always().RedirectStderr().Build();
+ *
+ * Although the builder could be used to dynamically set values. Example:
+ *
+ * CommandOptions::CommandOptionsBuilder options =
+ * CommandOptions::WithTimeout(10);
+ * if (!is_user_build()) {
+ * options.AsRoot();
+ * }
+ * RunCommand("command", {"args"}, options.Build());
+ */
+class CommandOptions {
+ private:
+ class CommandOptionsValues {
+ private:
+ CommandOptionsValues(int64_t timeout);
+
+ int64_t timeout_;
+ bool always_;
+ RootMode root_mode_;
+ StdoutMode stdout_mode_;
+ std::string logging_message_;
+
+ friend class CommandOptions;
+ friend class CommandOptionsBuilder;
+ };
+
+ CommandOptions(const CommandOptionsValues& values);
+
+ const CommandOptionsValues values;
+
+ public:
+ class CommandOptionsBuilder {
+ public:
+ /* Sets the command to always run, even on `dry-run` mode. */
+ CommandOptionsBuilder& Always();
+ /* Sets the command's RootMode as `SU_ROOT` */
+ CommandOptionsBuilder& AsRoot();
+ /* Sets the command's RootMode as `DROP_ROOT` */
+ CommandOptionsBuilder& DropRoot();
+ /* Sets the command's StdoutMode `REDIRECT_TO_STDERR` */
+ CommandOptionsBuilder& RedirectStderr();
+ /* When not empty, logs a message before executing the command.
+ * Must contain a `%s`, which will be replaced by the full command line, and end on `\n`. */
+ CommandOptionsBuilder& Log(const std::string& message);
+ /* Builds the command options. */
+ CommandOptions Build();
+
+ private:
+ CommandOptionsBuilder(int64_t timeout);
+ CommandOptionsValues values;
+ friend class CommandOptions;
+ };
+
+ /** Gets the command timeout, in seconds. */
+ int64_t Timeout() const;
+ /* Checks whether the command should always be run, even on dry-run mode. */
+ bool Always() const;
+ /** Gets the RootMode of the command. */
+ RootMode RootMode() const;
+ /** Gets the StdoutMode of the command. */
+ StdoutMode StdoutMode() const;
+ /** Gets the logging message header, it any. */
+ std::string LoggingMessage() const;
+
+ /** Creates a builder with the requied timeout. */
+ static CommandOptionsBuilder WithTimeout(int64_t timeout);
+
+ // Common options.
+ static CommandOptions DEFAULT;
+ static CommandOptions AS_ROOT_5;
+ static CommandOptions AS_ROOT_10;
+ static CommandOptions AS_ROOT_20;
+};
+
+/*
+ * Forks a command, waits for it to finish, and returns its status.
+ *
+ * |fd| file descriptor that receives the command's 'stdout'.
+ * |full_command| array containing the command (first entry) and its arguments.
+ * Must contain at least one element.
+ * |options| optional argument defining the command's behavior.
+ * |description| optional description of the command to be used on log messages. If empty,
+ * the command path (without arguments) will be used instead.
+ */
+int RunCommandToFd(int fd, const std::vector<const char*>& full_command,
+ const CommandOptions& options = CommandOptions::DEFAULT,
+ const std::string& description = "");
+
+/*
+ * Dumps the contents of a file into a file descriptor.
+ *
+ * |fd| file descriptor where the file is dumped into.
+ * |path| location of the file to be dumped.
+ */
+int DumpFileToFd(int fd, const std::string& path);
+
+#endif // FRAMEWORK_NATIVE_CMD_DUMPSTATE_UTIL_H_
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index ea70fe5..6348579 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -90,7 +90,7 @@
return ds.RunCommand(title, fullCommand, options);
}
static void RunDumpsys(const std::string& title, const std::vector<std::string>& dumpsysArgs,
- const CommandOptions& options = CommandOptions::DEFAULT_DUMPSYS,
+ const CommandOptions& options = Dumpstate::DEFAULT_DUMPSYS,
long dumpsysTimeout = 0) {
return ds.RunDumpsys(title, dumpsysArgs, options, dumpsysTimeout);
}
@@ -680,7 +680,8 @@
printf("Network: %s\n", network.c_str());
printf("Kernel: ");
- JustDumpFile("", "/proc/version");
+ fflush(stdout);
+ DumpFileToFd(STDOUT_FILENO, "/proc/version");
printf("Command line: %s\n", strtok(cmdline_buf, "\n"));
printf("Bugreport format version: %s\n", version_.c_str());
printf("Dumpstate info: id=%d pid=%d dry_run=%d args=%s extra_options=%s\n", id_, pid_,
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index 3d3d7ed..9f23a39 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -40,6 +40,7 @@
#include <android-base/macros.h>
#include <ziparchive/zip_writer.h>
+#include "DumpstateUtil.h"
#include "android/os/BnDumpstate.h"
// Workaround for const char *args[MAX_ARGS_ARRAY_SIZE] variables until they're converted to
@@ -53,28 +54,6 @@
#endif
/*
- * Defines the Linux user that should be executing a command.
- */
-enum RootMode {
- /* Explicitly change the `uid` and `gid` to be `shell`.*/
- DROP_ROOT,
- /* Don't change the `uid` and `gid`. */
- DONT_DROP_ROOT,
- /* Prefix the command with `/PATH/TO/su root`. Won't work non user builds. */
- SU_ROOT
-};
-
-/*
- * Defines what should happen with the `stdout` stream of a command.
- */
-enum StdoutMode {
- /* Don't change `stdout`. */
- NORMAL_STDOUT,
- /* Redirect `stdout` to `stderr`. */
- REDIRECT_TO_STDERR
-};
-
-/*
* Helper class used to report how long it takes for a section to finish.
*
* Typical usage:
@@ -102,88 +81,6 @@
};
/*
- * Value object used to set command options.
- *
- * Typically constructed using a builder with chained setters. Examples:
- *
- * CommandOptions::WithTimeout(20).AsRoot().Build();
- * CommandOptions::WithTimeout(10).Always().RedirectStderr().Build();
- *
- * Although the builder could be used to dynamically set values. Example:
- *
- * CommandOptions::CommandOptionsBuilder options =
- * CommandOptions::WithTimeout(10);
- * if (!is_user_build()) {
- * options.AsRoot();
- * }
- * RunCommand("command", {"args"}, options.Build());
- */
-class CommandOptions {
- private:
- class CommandOptionsValues {
- private:
- CommandOptionsValues(long timeout);
-
- long timeout_;
- bool always_;
- RootMode root_mode_;
- StdoutMode stdout_mode_;
- std::string logging_message_;
-
- friend class CommandOptions;
- friend class CommandOptionsBuilder;
- };
-
- CommandOptions(const CommandOptionsValues& values);
-
- const CommandOptionsValues values;
-
- public:
- class CommandOptionsBuilder {
- public:
- /* Sets the command to always run, even on `dry-run` mode. */
- CommandOptionsBuilder& Always();
- /* Sets the command's RootMode as `SU_ROOT` */
- CommandOptionsBuilder& AsRoot();
- /* Sets the command's RootMode as `DROP_ROOT` */
- CommandOptionsBuilder& DropRoot();
- /* Sets the command's StdoutMode `REDIRECT_TO_STDERR` */
- CommandOptionsBuilder& RedirectStderr();
- /* When not empty, logs a message before executing the command.
- * Must contain a `%s`, which will be replaced by the full command line, and end on `\n`. */
- CommandOptionsBuilder& Log(const std::string& message);
- /* Builds the command options. */
- CommandOptions Build();
-
- private:
- CommandOptionsBuilder(long timeout);
- CommandOptionsValues values;
- friend class CommandOptions;
- };
-
- /** Gets the command timeout, in seconds. */
- long Timeout() const;
- /* Checks whether the command should always be run, even on dry-run mode. */
- bool Always() const;
- /** Gets the RootMode of the command. */
- RootMode RootMode() const;
- /** Gets the StdoutMode of the command. */
- StdoutMode StdoutMode() const;
- /** Gets the logging message header, it any. */
- std::string LoggingMessage() const;
-
- /** Creates a builder with the requied timeout. */
- static CommandOptionsBuilder WithTimeout(long timeout);
-
- // Common options.
- static CommandOptions DEFAULT;
- static CommandOptions DEFAULT_DUMPSYS;
- static CommandOptions AS_ROOT_5;
- static CommandOptions AS_ROOT_10;
- static CommandOptions AS_ROOT_20;
-};
-
-/*
* Keeps track of current progress and estimated max, saving stats on file to tune up future runs.
*
* Each `dumpstate` section contributes to the total weight by an individual weight, so the overall
@@ -272,6 +169,8 @@
friend class DumpstateTest;
public:
+ static CommandOptions DEFAULT_DUMPSYS;
+
static Dumpstate& GetInstance();
/*
@@ -316,8 +215,7 @@
* timeout from `options`)
*/
void RunDumpsys(const std::string& title, const std::vector<std::string>& dumpsys_args,
- const CommandOptions& options = CommandOptions::DEFAULT_DUMPSYS,
- long dumpsys_timeout = 0);
+ const CommandOptions& options = DEFAULT_DUMPSYS, long dumpsys_timeout = 0);
/*
* Prints the contents of a file.
@@ -454,13 +352,6 @@
Dumpstate(const std::string& version = VERSION_CURRENT, bool dry_run = false,
const std::string& build_type = "user");
- // Internal version of RunCommand that just runs it, without updating progress.
- int JustRunCommand(const char* command, const char* path, std::vector<const char*>& args,
- const CommandOptions& options) const;
-
- // Internal version of RunCommand that just dumps it, without updating progress.
- int JustDumpFile(const std::string& title, const std::string& path) const;
-
// Whether this is a dry run.
bool dry_run_;
diff --git a/cmds/dumpstate/tests/dumpstate_test.cpp b/cmds/dumpstate/tests/dumpstate_test.cpp
index 0d68901..9613576 100644
--- a/cmds/dumpstate/tests/dumpstate_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_test.cpp
@@ -548,7 +548,7 @@
android::base::StringPrintf("%d %d\n", expected_runs, expected_average);
std::string actual_content;
ASSERT_TRUE(android::base::ReadFileToString(path, &actual_content))
- << "could not read statsfrom" << path;
+ << "could not read stats from " << path;
ASSERT_THAT(actual_content, StrEq(expected_content)) << "invalid stats on " << path;
}
};
diff --git a/cmds/dumpstate/utils.cpp b/cmds/dumpstate/utils.cpp
index b5f328d..c322d9f 100644
--- a/cmds/dumpstate/utils.cpp
+++ b/cmds/dumpstate/utils.cpp
@@ -97,12 +97,12 @@
static const long STATS_MAX_AVERAGE = 100000;
CommandOptions CommandOptions::DEFAULT = CommandOptions::WithTimeout(10).Build();
-CommandOptions CommandOptions::DEFAULT_DUMPSYS = CommandOptions::WithTimeout(30).Build();
+CommandOptions Dumpstate::DEFAULT_DUMPSYS = CommandOptions::WithTimeout(30).Build();
CommandOptions CommandOptions::AS_ROOT_5 = CommandOptions::WithTimeout(5).AsRoot().Build();
CommandOptions CommandOptions::AS_ROOT_10 = CommandOptions::WithTimeout(10).AsRoot().Build();
CommandOptions CommandOptions::AS_ROOT_20 = CommandOptions::WithTimeout(20).AsRoot().Build();
-CommandOptions::CommandOptionsBuilder::CommandOptionsBuilder(long timeout) : values(timeout) {
+CommandOptions::CommandOptionsBuilder::CommandOptionsBuilder(int64_t timeout) : values(timeout) {
}
CommandOptions::CommandOptionsBuilder& CommandOptions::CommandOptionsBuilder::Always() {
@@ -135,7 +135,7 @@
return CommandOptions(values);
}
-CommandOptions::CommandOptionsValues::CommandOptionsValues(long timeout)
+CommandOptions::CommandOptionsValues::CommandOptionsValues(int64_t timeout)
: timeout_(timeout),
always_(false),
root_mode_(DONT_DROP_ROOT),
@@ -146,7 +146,7 @@
CommandOptions::CommandOptions(const CommandOptionsValues& values) : values(values) {
}
-long CommandOptions::Timeout() const {
+int64_t CommandOptions::Timeout() const {
return values.timeout_;
}
@@ -166,7 +166,7 @@
return values.logging_message_;
}
-CommandOptions::CommandOptionsBuilder CommandOptions::WithTimeout(long timeout) {
+CommandOptions::CommandOptionsBuilder CommandOptions::WithTimeout(int64_t timeout) {
return CommandOptions::CommandOptionsBuilder(timeout);
}
@@ -657,9 +657,9 @@
}
// TODO: when converted to a Dumpstate function, it should be const
-static int _dump_file_from_fd(const std::string& title, const char* path, int fd) {
+static int _dump_file_from_fd_to_fd(const std::string& title, const char* path, int fd, int out_fd) {
if (!title.empty()) {
- printf("------ %s (%s", title.c_str(), path);
+ dprintf(out_fd, "------ %s (%s", title.c_str(), path);
struct stat st;
// Only show the modification time of non-device files.
@@ -671,9 +671,9 @@
char stamp[80];
time_t mtime = st.st_mtime;
strftime(stamp, sizeof(stamp), "%Y-%m-%d %H:%M:%S", localtime(&mtime));
- printf(": %s", stamp);
+ dprintf(out_fd, ": %s", stamp);
}
- printf(") ------\n");
+ dprintf(out_fd, ") ------\n");
}
bool newline = false;
@@ -688,24 +688,23 @@
uint64_t elapsed = DurationReporter::Nanotime();
int ret = TEMP_FAILURE_RETRY(select(fd + 1, &read_set, NULL, NULL, &tm));
if (ret == -1) {
- printf("*** %s: select failed: %s\n", path, strerror(errno));
+ dprintf(out_fd, "*** %s: select failed: %s\n", path, strerror(errno));
newline = true;
break;
} else if (ret == 0) {
elapsed = DurationReporter::Nanotime() - elapsed;
- printf("*** %s: Timed out after %.3fs\n", path,
- (float) elapsed / NANOS_PER_SEC);
+ dprintf(out_fd, "*** %s: Timed out after %.3fs\n", path, (float)elapsed / NANOS_PER_SEC);
newline = true;
break;
} else {
char buffer[65536];
ssize_t bytes_read = TEMP_FAILURE_RETRY(read(fd, buffer, sizeof(buffer)));
if (bytes_read > 0) {
- fwrite(buffer, bytes_read, 1, stdout);
+ android::base::WriteFully(out_fd, buffer, bytes_read);
newline = (buffer[bytes_read-1] == '\n');
} else {
if (bytes_read == -1) {
- printf("*** %s: Failed to read from fd: %s", path, strerror(errno));
+ dprintf(out_fd, "*** %s: Failed to read from fd: %s", path, strerror(errno));
newline = true;
}
break;
@@ -715,11 +714,31 @@
UpdateProgress(WEIGHT_FILE);
close(fd);
- if (!newline) printf("\n");
- if (!title.empty()) printf("\n");
+ if (!newline) dprintf(out_fd, "\n");
+ if (!title.empty()) dprintf(out_fd, "\n");
return 0;
}
+// Internal function used by both DumpFile and DumpFileToFd - the former wants to print title
+// information, while the later doesn't.
+static int DumpFileToFd(const std::string& title, int out_fd, const std::string& path) {
+ int fd = TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_NONBLOCK | O_CLOEXEC));
+ if (fd < 0) {
+ int err = errno;
+ if (title.empty()) {
+ printf("*** Error dumping %s: %s\n", path.c_str(), strerror(err));
+ } else {
+ printf("*** Error dumping %s (%s): %s\n", path.c_str(), title.c_str(), strerror(err));
+ }
+ return -1;
+ }
+ return _dump_file_from_fd_to_fd(title, path.c_str(), fd, out_fd);
+}
+
+int DumpFileToFd(int out_fd, const std::string& path) {
+ return DumpFileToFd("", out_fd, path);
+}
+
int Dumpstate::DumpFile(const std::string& title, const std::string& path) {
DurationReporter duration_reporter(title);
if (IsDryRun()) {
@@ -730,21 +749,7 @@
UpdateProgress(WEIGHT_FILE);
return 0;
}
- return JustDumpFile(title, path);
-}
-
-int Dumpstate::JustDumpFile(const std::string& title, const std::string& path) const {
- int fd = TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_NONBLOCK | O_CLOEXEC));
- if (fd < 0) {
- int err = errno;
- if (title.empty()) {
- printf("*** Error dumping %s: %s\n", path.c_str(), strerror(err));
- } else {
- printf("*** Error dumping %s (%s): %s\n", path.c_str(), title.c_str(), strerror(err));
- }
- return -1;
- }
- return _dump_file_from_fd(title, path.c_str(), fd);
+ return DumpFileToFd(title, STDOUT_FILENO, path);
}
int read_file_as_long(const char *path, long int *output) {
@@ -855,7 +860,7 @@
close(fd);
return -1;
}
- return _dump_file_from_fd(title, path, fd);
+ return _dump_file_from_fd_to_fd(title, path, fd, STDOUT_FILENO);
}
bool waitpid_with_timeout(pid_t pid, int timeout_seconds, int* status) {
@@ -909,6 +914,8 @@
return -1;
}
+ // TODO: SU_ROOT logic must be moved to RunCommandToFd
+
int size = full_command.size() + 1; // null terminated
int starting_index = 0;
if (options.RootMode() == SU_ROOT) {
@@ -935,7 +942,6 @@
}
}
args[i] = nullptr;
- const char* path = args[0];
const char* command = command_string.c_str();
if (options.RootMode() == SU_ROOT && ds.IsUserBuild()) {
@@ -963,7 +969,7 @@
return 0;
}
- int status = JustRunCommand(command, path, args, options);
+ int status = RunCommandToFd(STDOUT_FILENO, args, options, command);
/* TODO: for now we're simplifying the progress calculation by using the
* timeout as the weight. It's a good approximation for most cases, except when calling dumpsys,
@@ -978,8 +984,15 @@
return status;
}
-int Dumpstate::JustRunCommand(const char* command, const char* path, std::vector<const char*>& args,
- const CommandOptions& options) const {
+int RunCommandToFd(int fd, const std::vector<const char*>& full_command,
+ const CommandOptions& options, const std::string& description) {
+ if (full_command.empty()) {
+ MYLOGE("No arguments on RunCommandToFd'\n");
+ return -1;
+ }
+ const char* path = full_command[0];
+ const char* command = description.empty() ? path : description.c_str();
+
bool silent = (options.StdoutMode() == REDIRECT_TO_STDERR);
uint64_t start = DurationReporter::Nanotime();
@@ -1001,9 +1014,13 @@
return -1;
}
+ if (STDOUT_FILENO != fd) {
+ TEMP_FAILURE_RETRY(dup2(fd, STDOUT_FILENO));
+ close(fd);
+ }
if (silent) {
- // Redirect stderr to stdout
- dup2(STDERR_FILENO, STDOUT_FILENO);
+ // Redirect stderr to fd
+ dup2(STDERR_FILENO, fd);
}
/* make sure the child dies when dumpstate dies */
@@ -1015,11 +1032,10 @@
sigact.sa_handler = SIG_IGN;
sigaction(SIGPIPE, &sigact, NULL);
- execvp(path, (char**)args.data());
+ execvp(path, (char**)full_command.data());
// execvp's result will be handled after waitpid_with_timeout() below, but
// if it failed, it's safer to exit dumpstate.
MYLOGD("execvp on command '%s' failed (error: %s)\n", command, strerror(errno));
- fflush(stdout);
// Must call _exit (instead of exit), otherwise it will corrupt the zip
// file.
_exit(EXIT_FAILURE);
@@ -1028,6 +1044,8 @@
/* handle parent case */
int status;
bool ret = waitpid_with_timeout(pid, options.Timeout(), &status);
+ fsync(fd);
+
uint64_t elapsed = DurationReporter::Nanotime() - start;
if (!ret) {
if (errno == ETIMEDOUT) {