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