Add process control
This commit is contained in:
parent
d62822640c
commit
6b6dd58a0e
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
2
main.cpp
2
main.cpp
|
@ -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);
|
||||
|
|
15
misc.cpp
15
misc.cpp
|
@ -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
3
misc.h
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
Loading…
Reference in New Issue