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/fuchsia/fuchsia_trace_utils.h"
18
19 namespace perfetto {
20 namespace trace_processor {
21 namespace fuchsia_trace_utils {
22
23 namespace {
24 constexpr uint32_t kInlineStringMarker = 0x8000;
25 constexpr uint32_t kInlineStringLengthMask = 0x7FFF;
26 } // namespace
27
IsInlineString(uint32_t string_ref)28 bool IsInlineString(uint32_t string_ref) {
29 // Treat a string ref of 0 (the empty string) as inline. The empty string is
30 // not a true entry in the string table.
31 return (string_ref & kInlineStringMarker) || (string_ref == 0);
32 }
33
IsInlineThread(uint32_t thread_ref)34 bool IsInlineThread(uint32_t thread_ref) {
35 return thread_ref == 0;
36 }
37
38 // Converts a tick count to nanoseconds. Returns -1 if the result would not
39 // fit in a nonnegative int64_t. Negative timestamps are not allowed by the
40 // Fuchsia trace format. Also returns -1 if ticks_per_second is zero.
TicksToNs(uint64_t ticks,uint64_t ticks_per_second)41 int64_t TicksToNs(uint64_t ticks, uint64_t ticks_per_second) {
42 uint64_t ticks_hi = ticks >> 32;
43 uint64_t ticks_lo = ticks & ((uint64_t(1) << 32) - 1);
44 uint64_t ns_per_sec = 1000000000;
45 if (ticks_per_second == 0) {
46 return -1;
47 }
48 // This multiplication may overflow.
49 uint64_t result_hi = ticks_hi * ((ns_per_sec << 32) / ticks_per_second);
50 if (ticks_hi != 0 &&
51 result_hi / ticks_hi != ((ns_per_sec << 32) / ticks_per_second)) {
52 return -1;
53 }
54 // This computation never overflows, because ticks_lo is less than 2^32, and
55 // ns_per_sec = 10^9 < 2^32.
56 uint64_t result_lo = ticks_lo * ns_per_sec / ticks_per_second;
57 // Performing addition before the cast avoids undefined behavior.
58 int64_t result = static_cast<int64_t>(result_hi + result_lo);
59 // Check for addition overflow.
60 if (result < 0) {
61 return -1;
62 }
63 return result;
64 }
65
ToStorageVariadic(TraceStorage * storage) const66 Variadic ArgValue::ToStorageVariadic(TraceStorage* storage) const {
67 switch (type_) {
68 case ArgType::kNull:
69 return Variadic::String(storage->InternString("null"));
70 case ArgType::kInt32:
71 return Variadic::Integer(static_cast<int64_t>(int32_));
72 case ArgType::kUint32:
73 return Variadic::Integer(static_cast<int64_t>(uint32_));
74 case ArgType::kInt64:
75 return Variadic::Integer(int64_);
76 case ArgType::kUint64:
77 return Variadic::Integer(static_cast<int64_t>(uint64_));
78 case ArgType::kDouble:
79 return Variadic::Real(double_);
80 case ArgType::kString:
81 return Variadic::String(string_);
82 case ArgType::kPointer:
83 return Variadic::Pointer(pointer_);
84 case ArgType::kKoid:
85 return Variadic::Integer(static_cast<int64_t>(koid_));
86 case ArgType::kBool:
87 return Variadic::Boolean(bool_);
88 case ArgType::kUnknown:
89 return Variadic::String(storage->InternString("unknown"));
90 }
91 PERFETTO_FATAL("Not reached"); // Make GCC happy.
92 }
93
WordIndex()94 size_t RecordCursor::WordIndex() {
95 return word_index_;
96 }
97
SetWordIndex(size_t index)98 void RecordCursor::SetWordIndex(size_t index) {
99 word_index_ = index;
100 }
101
ReadTimestamp(uint64_t ticks_per_second,int64_t * ts_out)102 bool RecordCursor::ReadTimestamp(uint64_t ticks_per_second, int64_t* ts_out) {
103 const uint8_t* ts_data;
104 if (!ReadWords(1, &ts_data)) {
105 return false;
106 }
107 if (ts_out != nullptr) {
108 uint64_t ticks;
109 memcpy(&ticks, ts_data, sizeof(uint64_t));
110 *ts_out = TicksToNs(ticks, ticks_per_second);
111 }
112 return true;
113 }
114
ReadInlineString(uint32_t string_ref_or_len,base::StringView * string_out)115 bool RecordCursor::ReadInlineString(uint32_t string_ref_or_len,
116 base::StringView* string_out) {
117 // Note that this works correctly for the empty string, where string_ref is 0.
118 size_t len = string_ref_or_len & kInlineStringLengthMask;
119 size_t len_words = (len + 7) / 8;
120 const uint8_t* string_data;
121 if (!ReadWords(len_words, &string_data)) {
122 return false;
123 }
124 if (string_out != nullptr) {
125 *string_out =
126 base::StringView(reinterpret_cast<const char*>(string_data), len);
127 }
128 return true;
129 }
130
ReadInlineThread(FuchsiaThreadInfo * thread_out)131 bool RecordCursor::ReadInlineThread(FuchsiaThreadInfo* thread_out) {
132 const uint8_t* thread_data;
133 if (!ReadWords(2, &thread_data)) {
134 return false;
135 }
136 if (thread_out != nullptr) {
137 memcpy(&thread_out->pid, thread_data, sizeof(uint64_t));
138 memcpy(&thread_out->tid, thread_data + sizeof(uint64_t), sizeof(uint64_t));
139 }
140 return true;
141 }
142
ReadInt64(int64_t * out)143 bool RecordCursor::ReadInt64(int64_t* out) {
144 const uint8_t* out_data;
145 if (!ReadWords(1, &out_data)) {
146 return false;
147 }
148 if (out != nullptr) {
149 memcpy(out, out_data, sizeof(int64_t));
150 }
151 return true;
152 }
153
ReadUint64(uint64_t * out)154 bool RecordCursor::ReadUint64(uint64_t* out) {
155 const uint8_t* out_data;
156 if (!ReadWords(1, &out_data)) {
157 return false;
158 }
159 if (out != nullptr) {
160 memcpy(out, out_data, sizeof(uint64_t));
161 }
162 return true;
163 }
164
ReadDouble(double * out)165 bool RecordCursor::ReadDouble(double* out) {
166 static_assert(sizeof(double) == sizeof(uint64_t), "double must be 64 bits");
167
168 const uint8_t* out_data;
169 if (!ReadWords(1, &out_data)) {
170 return false;
171 }
172 if (out != nullptr) {
173 memcpy(out, out_data, sizeof(double));
174 }
175 return true;
176 }
177
ReadBlob(size_t num_bytes,std::vector<uint8_t> & append_buffer)178 bool RecordCursor::ReadBlob(size_t num_bytes,
179 std::vector<uint8_t>& append_buffer) {
180 const uint8_t* data = begin_ + sizeof(uint64_t) * word_index_;
181 auto num_words = (num_bytes + 7) / 8;
182 if (data + sizeof(uint64_t) * num_words > end_) {
183 return false;
184 }
185 word_index_ += num_words;
186 append_buffer.insert(append_buffer.end(), data, data + num_bytes);
187 return true;
188 }
189
ReadWords(size_t num_words,const uint8_t ** data_out)190 bool RecordCursor::ReadWords(size_t num_words, const uint8_t** data_out) {
191 const uint8_t* data = begin_ + sizeof(uint64_t) * word_index_;
192 // This addition is unconditional so that callers with data_out == nullptr do
193 // not necessarily have to check the return value, as future calls will fail
194 // due to attempting to read out of bounds.
195 word_index_ += num_words;
196 if (data + sizeof(uint64_t) * num_words <= end_) {
197 if (data_out != nullptr) {
198 *data_out = data;
199 }
200 return true;
201 } else {
202 return false;
203 }
204 }
205
206 } // namespace fuchsia_trace_utils
207 } // namespace trace_processor
208 } // namespace perfetto
209