| // Copyright 2017 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <stdint.h> |
| #include <stdio.h> |
| #include <unistd.h> |
| |
| #include <launchpad/launchpad.h> |
| #include <zircon/syscalls.h> |
| #include <fbl/algorithm.h> |
| |
| #include "utils.h" |
| |
| #if defined(__x86_64__) // entire file |
| |
| constexpr char ipt_program[] = "/system/bin/ipt"; |
| |
| // Format string for dump output file prefix. |
| // PT dumps consist of several files, all beginning with this prefix. |
| constexpr char pt_path_prefix[] = "/tmp/crash-pt"; |
| |
| // The format of the path prefix, without the file suffix. |
| // The full name of dump files is $pt_path_prefix.num$seq.$suffix. |
| #define PT_PATH_FORMAT "%s.num%d" |
| |
| // Test file suffix. This is the where PT buffer data is written. |
| // If $pt_path_prefix.$seq.$suffix doesn't exist then we use $seq. |
| constexpr char pt_file_test_suffix[] = "pt"; |
| |
| // Every dump is written to a new set of files: |
| // This counts to kMaxIptDumps and resets. |
| // When the max number of files has been written we don't write any more |
| // until at least one set of files has been deleted. |
| static int next_seq_num = 0; |
| constexpr int kMaxIptDumps = 4; |
| |
| // Don't wait forever for ipt to run. |
| // It may take awhile to dump the data. |
| // This seems to be a good number. |
| constexpr zx_time_t run_timeout = ZX_SEC(10); |
| |
| // Return the next sequence number to use or -1 if we've created the maximum |
| // number of dumps and can't make any more. |
| |
| static int next_free_seq_num() { |
| char test_file[sizeof(pt_path_prefix) + 10 + sizeof(pt_file_test_suffix)]; |
| |
| for (int i = 0; i < kMaxIptDumps; ++i) { |
| int seq = (i + next_seq_num) % kMaxIptDumps; |
| snprintf(test_file, sizeof(test_file), PT_PATH_FORMAT ".%s", |
| pt_path_prefix, seq, pt_file_test_suffix); |
| if (access(test_file, F_OK) < 0) |
| return seq; |
| } |
| |
| return -1; |
| } |
| |
| static zx_status_t crashlogger_run(const char* name, int argc, const char* const* argv) { |
| launchpad_t *lp; |
| const char* executable = argv[0]; |
| launchpad_create(ZX_HANDLE_INVALID, name, &lp); |
| launchpad_load_from_file(lp, executable); |
| launchpad_set_args(lp, argc, argv); |
| launchpad_clone(lp, LP_CLONE_ALL); |
| |
| zx_handle_t child; |
| const char* errmsg; |
| zx_status_t status = launchpad_go(lp, &child, &errmsg); |
| if (status != ZX_OK) |
| return status; |
| |
| zx_signals_t signals; |
| status = zx_object_wait_one(child, ZX_TASK_TERMINATED, zx_deadline_after(run_timeout), |
| &signals); |
| if (status != ZX_OK) { |
| // Leave reporting the error to the caller. |
| } else { |
| if (signals & ZX_TASK_TERMINATED) { |
| zx_info_process_t info; |
| status = zx_object_get_info(child, ZX_INFO_PROCESS, &info, |
| sizeof(info), nullptr, nullptr); |
| if (status == ZX_OK && info.exited) { |
| if (info.return_code != 0) { |
| // The child should have already printed its own error |
| // message, we just need to return some error code to the |
| // caller |
| status = ZX_ERR_IO; |
| } |
| } else { |
| // This shouldn't happen, but we don't want to kill crashlogger |
| // because of it. Return some indicative error code and let the |
| // caller report it. |
| status = ZX_ERR_BAD_STATE; |
| } |
| } else { |
| // This shouldn't happen, but we don't want to kill crashlogger |
| // because of it. Return some indicative error code and let the |
| // caller report it. |
| status = ZX_ERR_BAD_STATE; |
| } |
| } |
| |
| zx_handle_close(child); |
| return status; |
| } |
| |
| void try_dump_pt_data() { |
| printf("Hi, this is try_dump_pt_data\n"); |
| if (access(ipt_program, F_OK) != 0) { |
| // We only get called if dumping ipt is enabled. |
| // Thus it's not noise to print a warning here. |
| printf("Unable to dump PT data, missing PT control program: %s\n", ipt_program); |
| return; |
| } |
| |
| int seq_num = next_free_seq_num(); |
| if (seq_num < 0) { |
| printf("Unable to dump IPT data, maximum number of dumps made.\n"); |
| printf("To re-enable dumps, delete old ones by removing %s.*.\n", |
| pt_path_prefix); |
| return; |
| } |
| |
| constexpr char output_path_prefix_arg[] = "--output-path-prefix="; |
| char full_output_path_prefix_arg[sizeof(output_path_prefix_arg) + |
| sizeof(pt_path_prefix) + 10/*seq#*/]; |
| snprintf(full_output_path_prefix_arg, sizeof(full_output_path_prefix_arg), |
| "%s" PT_PATH_FORMAT, output_path_prefix_arg, |
| pt_path_prefix, seq_num); |
| |
| const char* const argv_pt_dump[] = { |
| ipt_program, |
| full_output_path_prefix_arg, |
| "--verbose=2", |
| "--control", |
| "stop", |
| "dump", |
| "start", |
| }; |
| zx_status_t status = crashlogger_run("ipt-dump", |
| fbl::count_of(argv_pt_dump), argv_pt_dump); |
| if (status == ZX_OK) { |
| printf("PT output written to " PT_PATH_FORMAT ".*\n", |
| pt_path_prefix, seq_num); |
| } else { |
| print_zx_error("Error dumping IPT data", status); |
| } |
| |
| // TODO(dje): It may be useful to break up the actions. |
| // E.g., if the dump fails we still want to turn IPT back on. |
| } |
| |
| #endif |