Add process control

This commit is contained in:
blankie 2023-02-02 19:50:28 +07:00
parent d62822640c
commit 6b6dd58a0e
Signed by: blankie
GPG Key ID: CC15FC822C7F61F5
9 changed files with 212 additions and 14 deletions

View File

@ -70,7 +70,7 @@ void event_loop(ImFont* monospace_font, Config& active_config, LogcatThread& log
}
// log_entries must not be mutated until the show logs button
main_window(log_entries_read == log_entries.size(), monospace_font,
main_window(log_entries_read == log_entries.size(), monospace_font, logcat_thread,
logcat_entries, filtered_logcat_entry_offsets,
active_config, inactive_config,
&show_settings_window, &show_filters_window, &show_exclusions_window, &show_logs_window, run_event_loop);

View File

@ -1,6 +1,8 @@
#include <cstring>
#include <fcntl.h>
#include <signal.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/epoll.h>
#include <system_error>
@ -72,7 +74,7 @@ static inline void handle_fd(int fd, char* buf, size_t* used,
}
LogcatThread::LogcatThread() {
LogcatThread::LogcatThread(const std::string* logcat_command) : _logcat_command(logcat_command) {
int fds[2];
struct epoll_event event = {.events = EPOLLIN | EPOLLET};
@ -233,8 +235,141 @@ void LogcatThread::_run_epoll_round() {
}
}
void LogcatThread::_try_reap(bool stop_requested) {
int wstatus;
int res = waitpid(this->_logcat_pid, &wstatus, WNOHANG);
if (res == -1) {
try {
throw_system_error("waitpid()");
} catch (const std::exception& e) {
LogEntry log_entry = {time(nullptr), e.what()};
print_log(log_entry);
this->_put_if_not_stopped(std::move(log_entry));
}
return;
} else if (res != this->_logcat_pid) {
return;
}
this->_logcat_pid = -1;
this->_logcat_process_kill_attempts = 0;
// just in case if the process was terminated mid-write
this->_stdout_buf_used = 0;
this->_stderr_buf_used = 0;
this->logcat_process_running.clear();
if (WIFEXITED(wstatus)) {
if (WEXITSTATUS(wstatus) && !stop_requested) {
LogEntry log_entry = {time(nullptr), std::string("Logcat exited with ") + std::to_string(WEXITSTATUS(wstatus))};
print_log(log_entry);
this->_put_if_not_stopped(std::move(log_entry));
}
} else if (WIFSIGNALED(wstatus)) {
if (!stop_requested) {
LogEntry log_entry = {time(nullptr), std::string("Logcat exited with -") + std::to_string(WTERMSIG(wstatus))};
print_log(log_entry);
this->_put_if_not_stopped(std::move(log_entry));
}
} else {
LogEntry log_entry = {time(nullptr), std::string("Logcat disappeared (wstatus=") + std::to_string(wstatus) + ')'};
print_log(log_entry);
this->_put_if_not_stopped(std::move(log_entry));
}
}
bool LogcatThread::_handle_stop_request() {
if (this->_logcat_pid == -1) {
return true;
}
int signal = this->_logcat_process_kill_attempts++ < 3 ? SIGTERM : SIGKILL;
if (!kill(this->_logcat_pid, signal)) {
return false;
}
try {
throw_system_error("kill()");
} catch (const std::exception& e) {
LogEntry log_entry = {time(nullptr), e.what()};
print_log(log_entry);
this->_put_if_not_stopped(std::move(log_entry));
}
return true;
}
bool LogcatThread::_handle_start_request() {
if (this->_logcat_pid != -1) {
this->_handle_stop_request();
return false;
}
auto dup2_or_die = [](int oldfd, int newfd) {
if (dup2(oldfd, newfd) == newfd) {
return;
}
try {
throw_system_error(std::string("dup2(") + std::to_string(oldfd) + ", " + std::to_string(newfd) + ')');
} catch (const std::exception& e) {
print_log({time(nullptr), e.what()});
}
exit(1);
};
auto close_or_warn = [](int fd) {
if (!close(fd)) {
return;
}
try {
throw_system_error(std::string("close(") + std::to_string(fd) + ')');
} catch (const std::exception& e) {
print_log({time(nullptr), e.what()});
}
};
this->_logcat_pid = fork();
if (this->_logcat_pid == -1) {
try {
throw_system_error("fork()");
} catch (const std::exception& e) {
LogEntry log_entry = {time(nullptr), e.what()};
print_log(log_entry);
this->_put_if_not_stopped(std::move(log_entry));
}
return true;
} else if (this->_logcat_pid == 0) {
dup2_or_die(this->_stderr_write_fd, 2);
dup2_or_die(this->_stdout_write_fd, 1);
close_or_warn(this->_stdout_write_fd);
close_or_warn(this->_stderr_write_fd);
close_or_warn(this->_stdout_read_fd);
close_or_warn(this->_stderr_read_fd);
execlp("sh", "sh", "-c", this->_logcat_command->c_str(), nullptr);
try {
throw_system_error("execlp()");
} catch (const std::exception& e) {
print_log({time(nullptr), e.what()});
}
exit(1);
} else {
this->logcat_process_running.test_and_set();
return true;
}
}
bool LogcatThread::_run_process_round(LogcatProcessRequest request) {
bool task_done;
switch (request) {
case LogcatProcessRequest::None: task_done = false; break;
case LogcatProcessRequest::Start: task_done = this->_handle_start_request(); break;
case LogcatProcessRequest::Stop: task_done = this->_handle_stop_request(); break;
};
if (this->_logcat_pid != -1) {
this->_try_reap(request == LogcatProcessRequest::Stop);
}
return task_done;
}
void LogcatThread::_run(std::stop_token stoken) {
while (!stoken.stop_requested()) {
while (!stoken.stop_requested() || this->_logcat_pid != -1) {
#ifndef NDEBUG
if (this->debug_log_request.test()) {
LogEntry log_entry = {time(nullptr), "A log entry from the logcat thread :D"};
@ -244,5 +379,11 @@ void LogcatThread::_run(std::stop_token stoken) {
#endif
this->_run_epoll_round();
if (stoken.stop_requested() && this->_logcat_pid != -1) {
this->_run_process_round(LogcatProcessRequest::Stop);
} else if (this->_run_process_round(this->logcat_process_request.load())) {
this->logcat_process_request.store(LogcatProcessRequest::None);
}
}
}

View File

@ -8,18 +8,26 @@
typedef std::variant<LogEntry, LogcatEntry> LogcatThreadItem;
#define NEWLINE_BUF_SIZE 512 * 1024
enum class LogcatProcessRequest {
None,
Start,
Stop,
};
class LogcatThread {
public:
// https://stackoverflow.com/a/2173764
LogcatThread(const LogcatThread&) = delete;
LogcatThread& operator=(const LogcatThread&) = delete;
LogcatThread();
LogcatThread(const std::string* logcat_command);
~LogcatThread();
void request_stop();
void join();
AtomicRingBuffer<LogcatThreadItem> atomic_ring_buffer;
std::atomic<LogcatProcessRequest> logcat_process_request = LogcatProcessRequest::None;
std::atomic_flag logcat_process_running;
#ifndef NDEBUG
std::atomic_flag debug_log_request;
@ -28,18 +36,29 @@ public:
private:
void _put_if_not_stopped(LogcatThreadItem item);
void _handle_line(char* buf, size_t length, bool is_stdout);
void _run_epoll_round();
void _run(std::stop_token stoken);
void _run_epoll_round();
void _try_reap(bool stop_requested);
bool _handle_stop_request();
bool _handle_start_request();
bool _run_process_round(LogcatProcessRequest request);
int _epoll_fd = -1;
int _stdout_read_fd = -1;
int _stdout_write_fd = -1;
int _stderr_read_fd = -1;
int _stderr_write_fd = -1;
char _stdout_buf[NEWLINE_BUF_SIZE];
size_t _stdout_buf_used = 0;
char _stderr_buf[NEWLINE_BUF_SIZE];
size_t _stderr_buf_used = 0;
pid_t _logcat_pid = -1;
char _logcat_process_kill_attempts = 0;
const std::string* _logcat_command;
Buffer _current_buffer = Buffer::Unknown;
std::stop_source _stop_source;
std::thread _thread;

View File

@ -134,7 +134,7 @@ int main(int, char**) {
bool run_event_loop = true;
LogcatThread logcat_thread = [&]() {
try {
return LogcatThread();
return LogcatThread(&config.logcat_command);
} catch (const std::exception& e) {
fprintf(stderr, "Failed to spawn logcat thread: %s\n", e.what());
exit(1);

View File

@ -42,8 +42,21 @@ bool ImGui::RedButton(const char* label) {
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, (ImVec4)ImColor::HSV(0.0f, 0.7f, 0.7f));
ImGui::PushStyleColor(ImGuiCol_ButtonActive, (ImVec4)ImColor::HSV(0.0f, 0.8f, 0.8f));
int res = ImGui::Button(label);
bool res = ImGui::Button(label);
ImGui::PopStyleColor(3);
return res;
}
bool ImGui::Button(const char* label, bool enabled) {
if (!enabled) {
ImGui::BeginDisabled();
}
bool res = ImGui::Button(label);
if (!enabled) {
ImGui::EndDisabled();
}
return res;
}

3
misc.h
View File

@ -10,5 +10,6 @@ void throw_system_error(std::string what);
namespace ImGui {
void TextUnformatted(const std::string& str);
bool RedButton(const char* text);
bool RedButton(const char* label);
bool Button(const char* label, bool enabled);
}; // namespace ImGui

View File

@ -5,6 +5,7 @@
#include "../misc.h"
#include "../logcat_entry.h"
#include "../logcat_thread.h"
#include "main.h"
static inline void render_table(ImFont* monospace_font, std::vector<LogcatEntry>& logcat_entries, std::vector<size_t>& filtered_logcat_entry_offsets) {
@ -52,7 +53,7 @@ static inline void render_table(ImFont* monospace_font, std::vector<LogcatEntry>
ImGui::EndTable();
}
void main_window(bool latest_log_entries_read, ImFont* monospace_font,
void main_window(bool latest_log_entries_read, ImFont* monospace_font, LogcatThread& logcat_thread,
std::vector<LogcatEntry>& logcat_entries, std::vector<size_t>& filtered_logcat_entry_offsets,
const Config& active_config, Config& inactive_config,
bool* show_settings_window, bool* show_filters_window, bool* show_exclusions_window, bool* show_logs_window, bool* run_event_loop) {
@ -71,19 +72,16 @@ void main_window(bool latest_log_entries_read, ImFont* monospace_font,
inactive_config.monospace_font_size = active_config.monospace_font_size;
*show_settings_window = true;
}
ImGui::SameLine();
if (ImGui::Button("Filters") && !*show_filters_window) {
copy_filters(inactive_config.filters, active_config.filters);
*show_filters_window = true;
}
ImGui::SameLine();
if (ImGui::Button("Exclusions") && !*show_exclusions_window) {
copy_filters(inactive_config.exclusions, active_config.exclusions);
*show_exclusions_window = true;
}
ImGui::SameLine();
bool open_logs;
if (!latest_log_entries_read) {
@ -95,6 +93,32 @@ void main_window(bool latest_log_entries_read, ImFont* monospace_font,
*show_logs_window = true;
}
bool can_send_logcat_request = logcat_thread.logcat_process_request.load() == LogcatProcessRequest::None;
bool logcat_running = logcat_thread.logcat_process_running.test();
ImGui::AlignTextToFramePadding();
ImGui::TextUnformatted("Logcat:");
ImGui::SameLine();
if (!can_send_logcat_request) {
ImGui::BeginDisabled();
}
if (ImGui::Button("Start", !logcat_running)) {
logcat_thread.logcat_process_request.store(LogcatProcessRequest::Start);
}
ImGui::SameLine();
if (ImGui::Button("Stop", logcat_running)) {
logcat_thread.logcat_process_request.store(LogcatProcessRequest::Stop);
}
ImGui::SameLine();
if (ImGui::Button("Restart", logcat_running)) {
logcat_thread.logcat_process_request.store(LogcatProcessRequest::Start);
}
if (!can_send_logcat_request) {
ImGui::EndDisabled();
}
ImGui::Separator();
// copied from imgui/imgui_demo.cpp: [SECTION] Example App: Debug Console / ShowExampleAppConsole()
// and [SECTION] Example App: Long Text / ShowExampleAppLongText()

View File

@ -5,8 +5,9 @@
#include "../config.h"
#include "../logcat_entry.h"
#include "../logcat_thread.h"
void main_window(bool latest_log_entries_read, ImFont* monospace_font,
void main_window(bool latest_log_entries_read, ImFont* monospace_font, LogcatThread& logcat_thread,
std::vector<LogcatEntry>& logcat_entries, std::vector<size_t>& filtered_logcat_entry_offsets,
const Config& active_config, Config& inactive_config,
bool* show_settings_window, bool* show_filters_window, bool* show_exclusions_window, bool* show_logs_window, bool* run_event_loop);

View File

@ -17,7 +17,6 @@ void settings_window(Config& active_config, Config& inactive_config, bool* p_ope
ImGui::End();
return;
}
// TODO actually have process control
ImGui::TextUnformatted("Logcat command only takes effect when logcat is not running");
ImGui::InputTextWithHint("Logcat command", "adb logcat -Dv 'threadtime UTC epoch usec uid'", &inactive_config.logcat_command);