| // Copyright 2019 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 "garnet/bin/trace2json/convert.h" |
| |
| #include <unistd.h> |
| |
| #include <cstddef> |
| #include <fstream> |
| #include <iostream> |
| #include <vector> |
| |
| #include <third_party/zlib/contrib/iostream3/zfstream.h> |
| #include <trace-reader/reader.h> |
| |
| #include "garnet/bin/trace2json/trace_parser.h" |
| #include "src/lib/fxl/logging.h" |
| |
| namespace { |
| |
| const char kLittleEndianMagicRecord[8] = {0x10, 0x00, 0x04, 0x46, 0x78, 0x54, 0x16, 0x00}; |
| |
| constexpr size_t kMagicSize = fbl::count_of(kLittleEndianMagicRecord); |
| constexpr uint64_t kMagicRecord = 0x0016547846040010; |
| |
| bool CompareMagic(const char* magic1, const char* magic2) { |
| for (size_t i = 0; i < kMagicSize; i++) { |
| if (magic1[i] != magic2[i]) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| } // namespace |
| |
| bool ConvertTrace(ConvertSettings settings) { |
| const uint64_t host_magic = kMagicRecord; |
| if (!CompareMagic(reinterpret_cast<const char*>(&host_magic), kLittleEndianMagicRecord)) { |
| FX_LOGS(ERROR) << "Detected big endian host. Aborting."; |
| return false; |
| } |
| |
| std::unique_ptr<std::ifstream> input_file_stream; |
| std::unique_ptr<std::ofstream> output_file_stream; |
| std::unique_ptr<gzifstream> input_gz_file_stream; |
| std::unique_ptr<gzofstream> output_gz_file_stream; |
| |
| std::istream* in_stream; |
| std::ostream* out_stream; |
| |
| if (!settings.input_file_name.empty()) { |
| if (settings.compressed_input) { |
| input_gz_file_stream = std::make_unique<gzifstream>( |
| settings.input_file_name.c_str(), std::ios_base::in | std::ios_base::binary); |
| if (!input_gz_file_stream->is_open()) { |
| FX_LOGS(ERROR) << "Error opening input file."; |
| return false; |
| } |
| in_stream = static_cast<std::istream*>(input_gz_file_stream.get()); |
| } else { |
| input_file_stream = std::make_unique<std::ifstream>( |
| settings.input_file_name.c_str(), std::ios_base::in | std::ios_base::binary); |
| if (!input_file_stream->is_open()) { |
| FX_LOGS(ERROR) << "Error opening input file."; |
| return false; |
| } |
| in_stream = static_cast<std::istream*>(input_file_stream.get()); |
| } |
| } else { |
| if (settings.compressed_input) { |
| input_gz_file_stream = |
| std::make_unique<gzifstream>(STDIN_FILENO, std::ios_base::in | std::ios_base::binary); |
| in_stream = static_cast<std::istream*>(input_gz_file_stream.get()); |
| } else { |
| in_stream = &std::cin; |
| } |
| } |
| |
| // Look for the magic number record at the start of the trace file and bail |
| // before opening (and thus truncating) the output file if we don't find it. |
| char initial_bytes[kMagicSize]; |
| if (in_stream->read(initial_bytes, kMagicSize).gcount() != kMagicSize) { |
| FX_LOGS(ERROR) << "Failed to read magic number."; |
| return false; |
| } |
| if (!CompareMagic(initial_bytes, kLittleEndianMagicRecord)) { |
| FX_LOGS(ERROR) << "Input file does not start with Fuchsia Trace magic " |
| "number. Aborting."; |
| return false; |
| } |
| |
| if (!settings.output_file_name.empty()) { |
| if (settings.compressed_output) { |
| output_gz_file_stream = std::make_unique<gzofstream>( |
| settings.output_file_name.c_str(), std::ios_base::out | std::ios_base::trunc); |
| if (!output_gz_file_stream->is_open()) { |
| FX_LOGS(ERROR) << "Error opening output file."; |
| return false; |
| } |
| out_stream = static_cast<std::ostream*>(output_gz_file_stream.get()); |
| } else { |
| output_file_stream = std::make_unique<std::ofstream>( |
| settings.output_file_name.c_str(), |
| std::ios_base::out | std::ios_base::binary | std::ios_base::trunc); |
| if (!output_file_stream->is_open()) { |
| FX_LOGS(ERROR) << "Error opening output file."; |
| return false; |
| } |
| out_stream = static_cast<std::ostream*>(output_file_stream.get()); |
| } |
| } else { |
| if (settings.compressed_output) { |
| // TODO(bhamrick): The gzofstream class we're using here does not appear to support writing to |
| // stdout properly. In trying to use STDOUT_FILENO similar to the usage of STDIN_FILENO above, |
| // it seemed that the output would either be truncated to a multiple of 0x1000 bytes, or not |
| // be written at all. |
| FX_LOGS(ERROR) << "Compressed output on stdout is not supported. Please " |
| "specify --output-file"; |
| return false; |
| } else { |
| out_stream = &std::cout; |
| } |
| } |
| |
| tracing::FuchsiaTraceParser parser(out_stream); |
| if (!parser.ParseComplete(in_stream)) { |
| return false; |
| } |
| |
| return true; |
| } |