1 /*
2 * Copyright (C) 2019 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "src/trace_processor/importers/gzip/gzip_trace_parser.h"
18
19 #include <string>
20
21 #include "perfetto/base/logging.h"
22 #include "perfetto/ext/base/string_utils.h"
23 #include "perfetto/ext/base/string_view.h"
24 #include "src/trace_processor/forwarding_trace_parser.h"
25 #include "src/trace_processor/util/status_macros.h"
26
27 namespace perfetto {
28 namespace trace_processor {
29
30 namespace {
31
32 using ResultCode = GzipDecompressor::ResultCode;
33
34 } // namespace
35
GzipTraceParser(TraceProcessorContext * context)36 GzipTraceParser::GzipTraceParser(TraceProcessorContext* context)
37 : context_(context) {}
38
GzipTraceParser(std::unique_ptr<ChunkedTraceReader> reader)39 GzipTraceParser::GzipTraceParser(std::unique_ptr<ChunkedTraceReader> reader)
40 : context_(nullptr), inner_(std::move(reader)) {}
41
42 GzipTraceParser::~GzipTraceParser() = default;
43
Parse(std::unique_ptr<uint8_t[]> data,size_t size)44 util::Status GzipTraceParser::Parse(std::unique_ptr<uint8_t[]> data,
45 size_t size) {
46 return ParseUnowned(data.get(), size);
47 }
48
ParseUnowned(const uint8_t * data,size_t size)49 util::Status GzipTraceParser::ParseUnowned(const uint8_t* data, size_t size) {
50 const uint8_t* start = data;
51 size_t len = size;
52
53 if (!inner_) {
54 PERFETTO_CHECK(context_);
55 inner_.reset(new ForwardingTraceParser(context_));
56 }
57
58 if (!first_chunk_parsed_) {
59 // .ctrace files begin with: "TRACE:\n" or "done. TRACE:\n" strip this if
60 // present.
61 base::StringView beginning(reinterpret_cast<const char*>(start), size);
62
63 static const char* kSystraceFileHeader = "TRACE:\n";
64 size_t offset = Find(kSystraceFileHeader, beginning);
65 if (offset != std::string::npos) {
66 start += strlen(kSystraceFileHeader) + offset;
67 len -= strlen(kSystraceFileHeader) + offset;
68 }
69 first_chunk_parsed_ = true;
70 }
71
72 // Our default uncompressed buffer size is 32MB as it allows for good
73 // throughput.
74 constexpr size_t kUncompressedBufferSize = 32 * 1024 * 1024;
75
76 needs_more_input_ = false;
77 decompressor_.SetInput(start, len);
78
79 for (auto ret = ResultCode::kOk; ret != ResultCode::kEof;) {
80 if (!buffer_) {
81 buffer_.reset(new uint8_t[kUncompressedBufferSize]);
82 bytes_written_ = 0;
83 }
84
85 auto result =
86 decompressor_.Decompress(buffer_.get() + bytes_written_,
87 kUncompressedBufferSize - bytes_written_);
88 ret = result.ret;
89 if (ret == ResultCode::kError || ret == ResultCode::kNoProgress)
90 return util::ErrStatus("Failed to decompress trace chunk");
91
92 if (ret == ResultCode::kNeedsMoreInput) {
93 PERFETTO_DCHECK(result.bytes_written == 0);
94 needs_more_input_ = true;
95 return util::OkStatus();
96 }
97 bytes_written_ += result.bytes_written;
98
99 if (bytes_written_ == kUncompressedBufferSize || ret == ResultCode::kEof)
100 RETURN_IF_ERROR(inner_->Parse(std::move(buffer_), bytes_written_));
101 }
102 return util::OkStatus();
103 }
104
NotifyEndOfFile()105 void GzipTraceParser::NotifyEndOfFile() {
106 // TODO(lalitm): this should really be an error returned to the caller but
107 // due to historical implementation, NotifyEndOfFile does not return a
108 // util::Status.
109 PERFETTO_DCHECK(!needs_more_input_);
110 PERFETTO_DCHECK(!buffer_);
111 }
112
113 } // namespace trace_processor
114 } // namespace perfetto
115