xref: /aosp_15_r20/external/perfetto/src/trace_processor/importers/fuchsia/fuchsia_trace_utils.cc (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
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