diff --git a/Applications/CMakeLists.txt b/Applications/CMakeLists.txt index 90a95f5010..76d2ae304e 100644 --- a/Applications/CMakeLists.txt +++ b/Applications/CMakeLists.txt @@ -3,6 +3,7 @@ add_subdirectory(Browser) add_subdirectory(Calculator) add_subdirectory(Calendar) add_subdirectory(CrashDaemon) +add_subdirectory(CrashReporter) add_subdirectory(Debugger) add_subdirectory(DisplaySettings) add_subdirectory(FileManager) diff --git a/Applications/CrashDaemon/main.cpp b/Applications/CrashDaemon/main.cpp index b2f0d3debc..857415bdd6 100644 --- a/Applications/CrashDaemon/main.cpp +++ b/Applications/CrashDaemon/main.cpp @@ -28,6 +28,8 @@ #include #include #include +#include +#include #include #include #include @@ -58,6 +60,18 @@ static void print_backtrace(const String& coredump_path) dbgln("{}", entry.to_string(true)); } +static void launch_crash_reporter(const String& coredump_path) +{ + pid_t child; + const char* argv[] = { "CrashReporter", coredump_path.characters(), nullptr, nullptr }; + if ((errno = posix_spawn(&child, "/bin/CrashReporter", nullptr, nullptr, const_cast(argv), environ))) { + perror("posix_spawn"); + } else { + if (disown(child) < 0) + perror("disown"); + } +} + int main() { static constexpr const char* coredumps_dir = "/tmp/coredump"; @@ -72,5 +86,6 @@ int main() dbgln("New coredump file: {}", coredump_path); wait_until_coredump_is_ready(coredump_path); print_backtrace(coredump_path); + launch_crash_reporter(coredump_path); } } diff --git a/Applications/CrashReporter/CMakeLists.txt b/Applications/CrashReporter/CMakeLists.txt new file mode 100644 index 0000000000..d391aa6b84 --- /dev/null +++ b/Applications/CrashReporter/CMakeLists.txt @@ -0,0 +1,10 @@ +compile_gml(CrashReporterWindow.gml CrashReporterWindowGML.h crash_reporter_window_gml) + + +set(SOURCES + main.cpp + CrashReporterWindowGML.h +) + +serenity_app(CrashReporter ICON app-crash-reporter) +target_link_libraries(CrashReporter LibCore LibCoreDump LibDesktop LibGUI) diff --git a/Applications/CrashReporter/CrashReporterWindow.gml b/Applications/CrashReporter/CrashReporterWindow.gml new file mode 100644 index 0000000000..7e2bd27cbd --- /dev/null +++ b/Applications/CrashReporter/CrashReporterWindow.gml @@ -0,0 +1,108 @@ +@GUI::Widget { + fill_with_background_color: true + + layout: @GUI::VerticalBoxLayout { + margins: [5, 5, 5, 5] + } + + @GUI::Widget { + vertical_size_policy: "Fixed" + preferred_height: 44 + + layout: @GUI::HorizontalBoxLayout { + spacing: 10 + } + + @GUI::ImageWidget { + name: "icon" + } + + @GUI::Label { + name: "description" + text_alignment: "CenterLeft" + } + } + + @GUI::Widget { + vertical_size_policy: "Fixed" + preferred_height: 18 + + layout: @GUI::HorizontalBoxLayout { + } + + @GUI::Label { + text: "Executable path:" + text_alignment: "CenterLeft" + horizontal_size_policy: "Fixed" + preferred_width: 90 + } + + @GUI::LinkLabel { + name: "executable_link" + text_alignment: "CenterLeft" + } + } + + @GUI::Widget { + vertical_size_policy: "Fixed" + preferred_height: 18 + + layout: @GUI::HorizontalBoxLayout { + } + + @GUI::Label { + text: "Coredump path:" + text_alignment: "CenterLeft" + horizontal_size_policy: "Fixed" + preferred_width: 90 + } + + @GUI::LinkLabel { + name: "coredump_link" + text_alignment: "CenterLeft" + } + } + + @GUI::Widget { + vertical_size_policy: "Fixed" + preferred_height: 18 + + layout: @GUI::HorizontalBoxLayout { + } + + @GUI::Label { + text: "Backtrace:" + text_alignment: "CenterLeft" + } + } + + @GUI::TextEditor { + name: "backtrace_text_editor" + mode: "ReadOnly" + } + + @GUI::Widget { + vertical_size_policy: "Fixed" + preferred_height: 32 + + layout: @GUI::HorizontalBoxLayout { + } + + // HACK: We need something like Layout::add_spacer() in GML! :^) + @GUI::Widget { + horizontal_size_policy: "Fixed" + vertical_size_policy: "Fill" + preferred_width: 378 + preferred_height: 0 + } + + @GUI::Button { + name: "close_button" + text: "Close" + horizontal_size_policy: "Fixed" + vertical_size_policy: "Fixed" + preferred_width: 70 + preferred_height: 22 + } + } +} diff --git a/Applications/CrashReporter/main.cpp b/Applications/CrashReporter/main.cpp new file mode 100644 index 0000000000..6d35cab725 --- /dev/null +++ b/Applications/CrashReporter/main.cpp @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2020, Linus Groh + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int main(int argc, char** argv) +{ + if (pledge("stdio shared_buffer accept cpath rpath unix fattr", nullptr) < 0) { + perror("pledge"); + return 1; + } + + const char* coredump_path = nullptr; + + Core::ArgsParser args_parser; + args_parser.set_general_help("Show information from an application crash coredump."); + args_parser.add_positional_argument(coredump_path, "Coredump path", "coredump-path"); + args_parser.parse(argc, argv); + + auto coredump = CoreDump::Reader::create(coredump_path); + if (!coredump) { + warnln("Could not open coredump '{}'", coredump_path); + return 1; + } + + auto app = GUI::Application::construct(argc, argv); + + if (pledge("stdio shared_buffer accept rpath unix", nullptr) < 0) { + perror("pledge"); + return 1; + } + + auto backtrace = coredump->backtrace(); + + String executable_path; + // FIXME: Maybe we should just embed the process's executable path + // in the coredump by itself so we don't have to extract it from the backtrace. + // Such a process section could also include the PID, which currently we'd have + // to parse from the filename. + if (!backtrace.entries().is_empty()) { + executable_path = backtrace.entries().last().object_name; + } else { + warnln("Could not determine executable path from coredump"); + return 1; + } + + if (unveil(executable_path.characters(), "r") < 0) { + perror("unveil"); + return 1; + } + + if (unveil("/res", "r") < 0) { + perror("unveil"); + return 1; + } + + if (unveil("/tmp/portal/launch", "rw") < 0) { + perror("unveil"); + return 1; + } + + if (unveil(nullptr, nullptr) < 0) { + perror("unveil"); + return 1; + } + + auto app_icon = GUI::Icon::default_icon("app-crash-reporter"); + + auto window = GUI::Window::construct(); + window->set_title("Crash Reporter"); + window->set_icon(app_icon.bitmap_for_size(16)); + window->set_resizable(false); + window->resize(460, 340); + window->center_on_screen(); + + auto& widget = window->set_main_widget(); + widget.load_from_gml(crash_reporter_window_gml); + + auto& icon_image_widget = static_cast(*widget.find_descendant_by_name("icon")); + icon_image_widget.set_bitmap(GUI::FileIconProvider::icon_for_executable(executable_path).bitmap_for_size(32)); + + auto app_name = LexicalPath(executable_path).basename(); + auto af = Desktop::AppFile::get_for_app(app_name); + if (af->is_valid()) + app_name = af->name(); + + auto& description_label = static_cast(*widget.find_descendant_by_name("description")); + description_label.set_text(String::formatted("\"{}\" has crashed!", app_name)); + + auto& executable_link_label = static_cast(*widget.find_descendant_by_name("executable_link")); + executable_link_label.set_text(LexicalPath::canonicalized_path(executable_path)); + executable_link_label.on_click = [&] { + Desktop::Launcher::open(URL::create_with_file_protocol(LexicalPath(executable_path).dirname())); + }; + + auto& coredump_link_label = static_cast(*widget.find_descendant_by_name("coredump_link")); + coredump_link_label.set_text(LexicalPath::canonicalized_path(coredump_path)); + coredump_link_label.on_click = [&] { + Desktop::Launcher::open(URL::create_with_file_protocol(LexicalPath(coredump_path).dirname())); + }; + + StringBuilder backtrace_builder; + auto first = true; + for (auto& entry : backtrace.entries()) { + if (first) + first = false; + else + backtrace_builder.append('\n'); + backtrace_builder.append(entry.to_string()); + } + + auto& backtrace_text_editor = static_cast(*widget.find_descendant_by_name("backtrace_text_editor")); + backtrace_text_editor.set_text(backtrace_builder.build()); + + auto& close_button = static_cast(*widget.find_descendant_by_name("close_button")); + close_button.on_click = [&](auto) { + app->quit(); + }; + + window->show(); + + return app->exec(); +} diff --git a/Base/res/icons/16x16/app-crash-reporter.png b/Base/res/icons/16x16/app-crash-reporter.png new file mode 100644 index 0000000000..8648098834 Binary files /dev/null and b/Base/res/icons/16x16/app-crash-reporter.png differ diff --git a/Base/res/icons/32x32/app-crash-reporter.png b/Base/res/icons/32x32/app-crash-reporter.png new file mode 100644 index 0000000000..a54fd30a86 Binary files /dev/null and b/Base/res/icons/32x32/app-crash-reporter.png differ