liblogcat: add android_logcat_run_command_thread
A non-blocking API to run a logcat function in a background thread. Returns a read end of a pipe to collect the output. Test: gTest logcat-unit-tests Bug: 35326290 Change-Id: Idc14e4ad955e0b2b9fafa5d3aeed8cd7fb4069fb
This commit is contained in:
parent
c0cf90d1fb
commit
1d6928b736
2 changed files with 181 additions and 6 deletions
|
@ -76,9 +76,19 @@ android_logcat_context create_android_logcat();
|
|||
int android_logcat_run_command(android_logcat_context ctx, int output, int error,
|
||||
int argc, char* const* argv, char* const* envp);
|
||||
|
||||
/* Will not block, performed in-process
|
||||
*
|
||||
* Starts a thread, opens a pipe, returns reading end fd, saves off argv.
|
||||
* The command supports 2>&1 (mix content) and 2>/dev/null (drop content) for
|
||||
* scripted error (stderr) redirection.
|
||||
*/
|
||||
int android_logcat_run_command_thread(android_logcat_context ctx, int argc,
|
||||
char* const* argv, char* const* envp);
|
||||
int android_logcat_run_command_thread_running(android_logcat_context ctx);
|
||||
|
||||
/* Finished with context
|
||||
*
|
||||
* Free up all associated resources.
|
||||
* Kill the command thread ASAP (if any), and free up all associated resources.
|
||||
*
|
||||
* Return value is the result of the android_logcat_run_command, or
|
||||
* non-zero for any errors.
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include <fcntl.h>
|
||||
#include <getopt.h>
|
||||
#include <math.h>
|
||||
#include <pthread.h>
|
||||
#include <sched.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
|
@ -35,8 +36,10 @@
|
|||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <android-base/file.h>
|
||||
#include <android-base/stringprintf.h>
|
||||
|
@ -55,18 +58,25 @@
|
|||
|
||||
struct android_logcat_context_internal {
|
||||
// status
|
||||
int retval;
|
||||
// Arguments passed in
|
||||
volatile std::atomic_int retval; // valid if thread_stopped set
|
||||
// Arguments passed in, or copies and storage thereof if a thread.
|
||||
int argc;
|
||||
char* const* argv;
|
||||
char* const* envp;
|
||||
std::vector<std::string> args;
|
||||
std::vector<const char*> argv_hold;
|
||||
std::vector<std::string> envs;
|
||||
std::vector<const char*> envp_hold;
|
||||
int output_fd;
|
||||
int error_fd;
|
||||
|
||||
// library
|
||||
FILE* output; // everything writes to fileno(output), buffer unused
|
||||
FILE* error; // unless error == output.
|
||||
bool stop; // quick exit flag
|
||||
int fds[2]; // From popen call
|
||||
FILE* output; // everything writes to fileno(output), buffer unused
|
||||
FILE* error; // unless error == output.
|
||||
pthread_t thr;
|
||||
volatile std::atomic_bool stop; // quick exit flag
|
||||
volatile std::atomic_bool thread_stopped;
|
||||
bool stderr_null; // shell "2>/dev/null"
|
||||
bool stderr_stdout; // shell "2>&1"
|
||||
|
||||
|
@ -100,10 +110,17 @@ android_logcat_context create_android_logcat() {
|
|||
1, sizeof(android_logcat_context_internal));
|
||||
if (!context) return NULL;
|
||||
|
||||
context->fds[0] = -1;
|
||||
context->fds[1] = -1;
|
||||
context->output_fd = -1;
|
||||
context->error_fd = -1;
|
||||
context->maxRotatedLogs = DEFAULT_MAX_ROTATED_LOGS;
|
||||
|
||||
context->argv_hold.clear();
|
||||
context->args.clear();
|
||||
context->envp_hold.clear();
|
||||
context->envs.clear();
|
||||
|
||||
return (android_logcat_context)context;
|
||||
}
|
||||
|
||||
|
@ -160,12 +177,18 @@ static void close_output(android_logcat_context_internal* context) {
|
|||
if (context->output_fd == fileno(context->output)) {
|
||||
context->output_fd = -1;
|
||||
}
|
||||
if (context->fds[1] == fileno(context->output)) {
|
||||
context->fds[1] = -1;
|
||||
}
|
||||
fclose(context->output);
|
||||
}
|
||||
context->output = NULL;
|
||||
}
|
||||
if (context->output_fd >= 0) {
|
||||
if (context->output_fd != fileno(stdout)) {
|
||||
if (context->fds[1] == context->output_fd) {
|
||||
context->fds[1] = -1;
|
||||
}
|
||||
close(context->output_fd);
|
||||
}
|
||||
context->output_fd = -1;
|
||||
|
@ -190,6 +213,9 @@ static void close_error(android_logcat_context_internal* context) {
|
|||
if (context->error_fd == fileno(context->error)) {
|
||||
context->error_fd = -1;
|
||||
}
|
||||
if (context->fds[1] == fileno(context->error)) {
|
||||
context->fds[1] = -1;
|
||||
}
|
||||
fclose(context->error);
|
||||
}
|
||||
context->error = NULL;
|
||||
|
@ -197,6 +223,9 @@ static void close_error(android_logcat_context_internal* context) {
|
|||
if (context->error_fd >= 0) {
|
||||
if ((context->error_fd != fileno(stdout)) &&
|
||||
(context->error_fd != fileno(stderr))) {
|
||||
if (context->fds[1] == context->error_fd) {
|
||||
context->fds[1] = -1;
|
||||
}
|
||||
close(context->error_fd);
|
||||
}
|
||||
context->error_fd = -1;
|
||||
|
@ -1577,6 +1606,21 @@ close:
|
|||
android_logger_list_free(logger_list);
|
||||
|
||||
exit:
|
||||
// close write end of pipe to help things along
|
||||
if (context->output_fd == context->fds[1]) {
|
||||
android::close_output(context);
|
||||
}
|
||||
if (context->error_fd == context->fds[1]) {
|
||||
android::close_error(context);
|
||||
}
|
||||
if (context->fds[1] >= 0) {
|
||||
// NB: should be closed by the above
|
||||
int save_errno = errno;
|
||||
close(context->fds[1]);
|
||||
errno = save_errno;
|
||||
context->fds[1] = -1;
|
||||
}
|
||||
context->thread_stopped = true;
|
||||
return context->retval;
|
||||
}
|
||||
|
||||
|
@ -1593,9 +1637,111 @@ int android_logcat_run_command(android_logcat_context ctx,
|
|||
context->argv = argv;
|
||||
context->envp = envp;
|
||||
context->stop = false;
|
||||
context->thread_stopped = false;
|
||||
return __logcat(context);
|
||||
}
|
||||
|
||||
// starts a thread, opens a pipe, returns reading end.
|
||||
int android_logcat_run_command_thread(android_logcat_context ctx,
|
||||
int argc, char* const* argv,
|
||||
char* const* envp) {
|
||||
android_logcat_context_internal* context = ctx;
|
||||
|
||||
int save_errno = EBUSY;
|
||||
if ((context->fds[0] >= 0) || (context->fds[1] >= 0)) {
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (pipe(context->fds) < 0) {
|
||||
save_errno = errno;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
pthread_attr_t attr;
|
||||
if (pthread_attr_init(&attr)) {
|
||||
save_errno = errno;
|
||||
goto close_exit;
|
||||
}
|
||||
|
||||
struct sched_param param;
|
||||
memset(¶m, 0, sizeof(param));
|
||||
pthread_attr_setschedparam(&attr, ¶m);
|
||||
pthread_attr_setschedpolicy(&attr, SCHED_BATCH);
|
||||
if (pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)) {
|
||||
int save_errno = errno;
|
||||
goto pthread_attr_exit;
|
||||
}
|
||||
|
||||
context->stop = false;
|
||||
context->thread_stopped = false;
|
||||
context->output_fd = context->fds[1];
|
||||
// save off arguments so they remain while thread is active.
|
||||
for (int i = 0; i < argc; ++i) {
|
||||
context->args.push_back(std::string(argv[i]));
|
||||
}
|
||||
// save off environment so they remain while thread is active.
|
||||
if (envp) for (size_t i = 0; envp[i]; ++i) {
|
||||
context->envs.push_back(std::string(envp[i]));
|
||||
}
|
||||
|
||||
for (auto& str : context->args) {
|
||||
context->argv_hold.push_back(str.c_str());
|
||||
}
|
||||
context->argv_hold.push_back(NULL);
|
||||
for (auto& str : context->envs) {
|
||||
context->envp_hold.push_back(str.c_str());
|
||||
}
|
||||
context->envp_hold.push_back(NULL);
|
||||
|
||||
context->argc = context->argv_hold.size() - 1;
|
||||
context->argv = (char* const*)&context->argv_hold[0];
|
||||
context->envp = (char* const*)&context->envp_hold[0];
|
||||
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "argv[%d] = {", context->argc);
|
||||
for (auto str : context->argv_hold) {
|
||||
fprintf(stderr, " \"%s\"", str ?: "NULL");
|
||||
}
|
||||
fprintf(stderr, " }\n");
|
||||
fflush(stderr);
|
||||
#endif
|
||||
context->retval = EXIT_SUCCESS;
|
||||
if (pthread_create(&context->thr, &attr,
|
||||
(void*(*)(void*))__logcat, context)) {
|
||||
int save_errno = errno;
|
||||
goto argv_exit;
|
||||
}
|
||||
pthread_attr_destroy(&attr);
|
||||
|
||||
return context->fds[0];
|
||||
|
||||
argv_exit:
|
||||
context->argv_hold.clear();
|
||||
context->args.clear();
|
||||
context->envp_hold.clear();
|
||||
context->envs.clear();
|
||||
pthread_attr_exit:
|
||||
pthread_attr_destroy(&attr);
|
||||
close_exit:
|
||||
close(context->fds[0]);
|
||||
context->fds[0] = -1;
|
||||
close(context->fds[1]);
|
||||
context->fds[1] = -1;
|
||||
exit:
|
||||
errno = save_errno;
|
||||
context->stop = true;
|
||||
context->thread_stopped = true;
|
||||
context->retval = EXIT_FAILURE;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// test if the thread is still doing 'stuff'
|
||||
int android_logcat_run_command_thread_running(android_logcat_context ctx) {
|
||||
android_logcat_context_internal* context = ctx;
|
||||
|
||||
return context->thread_stopped == false;
|
||||
}
|
||||
|
||||
// Finished with context
|
||||
int android_logcat_destroy(android_logcat_context* ctx) {
|
||||
android_logcat_context_internal* context = *ctx;
|
||||
|
@ -1604,9 +1750,28 @@ int android_logcat_destroy(android_logcat_context* ctx) {
|
|||
|
||||
context->stop = true;
|
||||
|
||||
while (context->thread_stopped == false) {
|
||||
sched_yield();
|
||||
}
|
||||
|
||||
delete context->regex;
|
||||
context->argv_hold.clear();
|
||||
context->args.clear();
|
||||
context->envp_hold.clear();
|
||||
context->envs.clear();
|
||||
if (context->fds[0] >= 0) {
|
||||
close(context->fds[0]);
|
||||
context->fds[0] = -1;
|
||||
}
|
||||
android::close_output(context);
|
||||
android::close_error(context);
|
||||
if (context->fds[1] >= 0) {
|
||||
// NB: could be closed by the above fclose(s), ignore error.
|
||||
int save_errno = errno;
|
||||
close(context->fds[1]);
|
||||
errno = save_errno;
|
||||
context->fds[1] = -1;
|
||||
}
|
||||
|
||||
android_closeEventTagMap(context->eventTagMap);
|
||||
|
||||
|
|
Loading…
Reference in a new issue