1 /**
2 * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "trace.h"
17
18 #include "include/runtime_notification.h"
19 #include "include/runtime.h"
20 #include "include/method.h"
21 #include "include/class.h"
22 #include "plugins.h"
23 #include "runtime/include/runtime.h"
24
25 #include <string>
26
27 namespace panda {
28
29 Trace *volatile Trace::singleton_trace = nullptr;
30 bool Trace::is_tracing = false;
31 // NOLINTNEXTLINE(fuchsia-statically-constructed-objects)
32 LanguageContext Trace::ctx = LanguageContext(nullptr);
33
SystemMicroSecond()34 static uint64_t SystemMicroSecond()
35 {
36 timespec current {};
37 clock_gettime(CLOCK_MONOTONIC, ¤t);
38 // TODO(ht): Deleting OS-specific code, move to libpandabase, issue #3210
39 return static_cast<uint64_t>(current.tv_sec) * UINT64_C(1000000) + // 1000000 - time
40 current.tv_nsec / UINT64_C(1000); // 1000 - time
41 }
42
RealTimeSecond()43 static uint64_t RealTimeSecond()
44 {
45 timespec current {};
46 clock_gettime(CLOCK_REALTIME, ¤t);
47 // TODO(ht): Deleting OS-specific code, move to libpandabase, issue #3210
48 return static_cast<uint64_t>(current.tv_sec);
49 }
50
ThreadCpuClock(os::thread::native_handle_type thread,clockid_t * clock_id)51 static void ThreadCpuClock(os::thread::native_handle_type thread, clockid_t *clock_id)
52 {
53 pthread_getcpuclockid(thread, clock_id);
54 }
GetCpuMicroSecond()55 static uint64_t GetCpuMicroSecond()
56 {
57 auto thread_self = os::thread::GetNativeHandle();
58 clockid_t clock_id;
59 ThreadCpuClock(thread_self, &clock_id);
60 timespec current {};
61 clock_gettime(clock_id, ¤t);
62 // TODO(ht): Deleting OS-specific code, move to libpandabase, issue #3210
63 return static_cast<uint64_t>(current.tv_sec) * UINT64_C(1000000) + // 1000000 - time
64 current.tv_nsec / UINT64_C(1000); // 1000 - time
65 }
66
Trace(PandaUniquePtr<panda::os::unix::file::File> trace_file,size_t buffer_size)67 Trace::Trace(PandaUniquePtr<panda::os::unix::file::File> trace_file, size_t buffer_size)
68 : trace_file_(std::move(trace_file)),
69 BUFFER_SIZE(std::max(TRACE_HEADER_REAL_LENGTH, buffer_size)),
70 TRACE_START_TIME(SystemMicroSecond()),
71 BASE_CPU_TIME(GetCpuMicroSecond()),
72 buffer_offset_(0),
73 overbrim_(false)
74 {
75 buffer_ = MakePandaUnique<uint8_t[]>(BUFFER_SIZE); // NOLINT(modernize-avoid-c-arrays)
76 if (memset_s(buffer_.get(), BUFFER_SIZE, 0, TRACE_HEADER_LENGTH) != EOK) {
77 LOG(ERROR, RUNTIME) << __func__ << " memset error";
78 }
79 WriteDataByte(buffer_.get(), MAGIC_VALUE, NUMBER_OF_4BYTES);
80 WriteDataByte(buffer_.get() + NUMBER_OF_4BYTES, TRACE_VERSION, NUMBER_OF_2BYTES);
81 WriteDataByte(buffer_.get() + NUMBER_OF_4BYTES + NUMBER_OF_2BYTES, TRACE_HEADER_LENGTH, NUMBER_OF_2BYTES);
82 WriteDataByte(buffer_.get() + NUMBER_OF_8BYTES, TRACE_START_TIME, NUMBER_OF_8BYTES);
83 WriteDataByte(buffer_.get() + NUMBER_OF_8BYTES + NUMBER_OF_8BYTES, TRACE_ITEM_SIZE, NUMBER_OF_2BYTES);
84 // Atomic with relaxed order reason: data race with buffer_offset_ with no synchronization or ordering constraints
85 // imposed on other reads or writes
86 buffer_offset_.store(TRACE_HEADER_LENGTH, std::memory_order_relaxed);
87 }
88
89 Trace::~Trace() = default;
90
StartTracing(const char * trace_filename,size_t buffer_size)91 void Trace::StartTracing(const char *trace_filename, size_t buffer_size)
92 {
93 os::memory::LockHolder lock(trace_lock);
94 if (singleton_trace != nullptr) {
95 LOG(ERROR, RUNTIME) << "method tracing is running, ignoring new request";
96 return;
97 }
98
99 PandaString file_name(trace_filename);
100 if (file_name.empty()) {
101 file_name = "method";
102 file_name = file_name + ConvertToString(std::to_string(RealTimeSecond()));
103 file_name += ".trace";
104 #ifdef PANDA_TARGET_MOBILE
105 file_name = "/data/misc/trace/" + file_name;
106 #endif // PANDA_TARGET_MOBILE
107 }
108
109 auto trace_file = MakePandaUnique<panda::os::unix::file::File>(
110 panda::os::file::Open(file_name, panda::os::file::Mode::READWRITECREATE).GetFd());
111 if (!trace_file->IsValid()) {
112 LOG(ERROR, RUNTIME) << "Cannot OPEN/CREATE the trace file " << file_name;
113 return;
114 }
115
116 panda_file::SourceLang lang = panda::plugins::RuntimeTypeToLang(Runtime::GetRuntimeType());
117 ctx = Runtime::GetCurrent()->GetLanguageContext(lang);
118
119 singleton_trace = ctx.CreateTrace(std::move(trace_file), buffer_size);
120
121 Runtime::GetCurrent()->GetNotificationManager()->AddListener(singleton_trace,
122 RuntimeNotificationManager::Event::METHOD_EVENTS);
123 is_tracing = true;
124 }
125
StopTracing()126 void Trace::StopTracing()
127 {
128 os::memory::LockHolder lock(trace_lock);
129 if (singleton_trace == nullptr) {
130 LOG(ERROR, RUNTIME) << "need to stop method tracing, but no method tracing is running";
131 } else {
132 Runtime::GetCurrent()->GetNotificationManager()->RemoveListener(
133 singleton_trace, RuntimeNotificationManager::Event::METHOD_EVENTS);
134 singleton_trace->SaveTracingData();
135 is_tracing = false;
136 Runtime::GetCurrent()->GetInternalAllocator()->Delete(singleton_trace);
137 singleton_trace = nullptr;
138 }
139 }
140
TriggerTracing()141 void Trace::TriggerTracing()
142 {
143 if (!is_tracing) {
144 Trace::StartTracing("", FILE_SIZE);
145 } else {
146 Trace::StopTracing();
147 }
148 }
149
RecordThreadsInfo(PandaOStringStream * os)150 void Trace::RecordThreadsInfo([[maybe_unused]] PandaOStringStream *os)
151 {
152 os::memory::LockHolder lock(thread_info_lock_);
153 for (const auto &info : thread_info_set_) {
154 (*os) << info;
155 }
156 }
GetAverageTime()157 uint64_t Trace::GetAverageTime()
158 {
159 uint64_t begin = GetCpuMicroSecond();
160 for (int i = 0; i < LOOP_NUMBER; i++) {
161 GetCpuMicroSecond();
162 }
163 uint64_t delay = GetCpuMicroSecond() - begin;
164 return delay / DIVIDE_NUMBER;
165 }
166
RecordMethodsInfo(PandaOStringStream * os,const PandaSet<Method * > & called_methods)167 void Trace::RecordMethodsInfo(PandaOStringStream *os, const PandaSet<Method *> &called_methods)
168 {
169 for (const auto &method : called_methods) {
170 (*os) << GetMethodDetailInfo(method);
171 }
172 }
173
WriteInfoToBuf(const ManagedThread * thread,Method * method,EventFlag event,uint32_t thread_time,uint32_t real_time)174 void Trace::WriteInfoToBuf(const ManagedThread *thread, Method *method, EventFlag event, uint32_t thread_time,
175 uint32_t real_time)
176 {
177 int32_t write_after_offset;
178 int32_t write_before_offset;
179
180 // Atomic with relaxed order reason: data race with buffer_offset_ with no synchronization or ordering constraints
181 // imposed on other reads or writes
182 write_before_offset = buffer_offset_.load(std::memory_order_relaxed);
183 do {
184 write_after_offset = write_before_offset + TRACE_ITEM_SIZE;
185 if (BUFFER_SIZE < static_cast<size_t>(write_after_offset)) {
186 overbrim_ = true;
187 return;
188 }
189 } while (!buffer_offset_.compare_exchange_weak(write_before_offset, write_after_offset, std::memory_order_relaxed));
190
191 EventFlag flag = TRACE_METHOD_ENTER;
192 switch (event) {
193 case EventFlag::TRACE_METHOD_ENTER:
194 flag = TRACE_METHOD_ENTER;
195 break;
196 case EventFlag::TRACE_METHOD_EXIT:
197 flag = TRACE_METHOD_EXIT;
198 break;
199 case EventFlag::TRACE_METHOD_UNWIND:
200 flag = TRACE_METHOD_UNWIND;
201 break;
202 default:
203 LOG(ERROR, RUNTIME) << "unrecognized events" << std::endl;
204 }
205 uint32_t method_action_value = EncodeMethodAndEventToId(method, flag);
206
207 uint8_t *ptr = buffer_.get() + write_before_offset;
208 WriteDataByte(ptr, thread->GetId(), NUMBER_OF_2BYTES);
209 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
210 ptr += NUMBER_OF_2BYTES;
211 WriteDataByte(ptr, method_action_value, NUMBER_OF_4BYTES);
212 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
213 ptr += NUMBER_OF_4BYTES;
214 WriteDataByte(ptr, thread_time, NUMBER_OF_4BYTES);
215 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
216 ptr += NUMBER_OF_4BYTES;
217 WriteDataByte(ptr, real_time, NUMBER_OF_4BYTES);
218 }
219
EncodeMethodAndEventToId(Method * method,EventFlag flag)220 uint32_t Trace::EncodeMethodAndEventToId(Method *method, EventFlag flag)
221 {
222 uint32_t method_action_id = (EncodeMethodToId(method) << ENCODE_EVENT_BITS) | flag;
223 ASSERT(method == DecodeIdToMethod(method_action_id));
224 return method_action_id;
225 }
226
EncodeMethodToId(Method * method)227 uint32_t Trace::EncodeMethodToId(Method *method)
228 {
229 os::memory::LockHolder lock(methods_lock_);
230 uint32_t method_id_value;
231 auto iter = method_id_pandamap_.find(method);
232 if (iter != method_id_pandamap_.end()) {
233 method_id_value = iter->second;
234 } else {
235 method_id_value = methods_called_vector_.size();
236 methods_called_vector_.push_back(method);
237 method_id_pandamap_.emplace(method, method_id_value);
238 }
239 return method_id_value;
240 }
241
DecodeIdToMethod(uint32_t id)242 Method *Trace::DecodeIdToMethod(uint32_t id)
243 {
244 os::memory::LockHolder lock(methods_lock_);
245 return methods_called_vector_[id >> ENCODE_EVENT_BITS];
246 }
247
GetUniqueMethods(size_t buffer_length,PandaSet<Method * > * called_methods_set)248 void Trace::GetUniqueMethods(size_t buffer_length, PandaSet<Method *> *called_methods_set)
249 {
250 uint8_t *data_begin = buffer_.get() + TRACE_HEADER_LENGTH;
251 uint8_t *data_end = buffer_.get() + buffer_length;
252
253 while (data_begin < data_end) {
254 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
255 uint32_t decoded_data = GetDataFromBuffer(data_begin + NUMBER_OF_2BYTES, NUMBER_OF_4BYTES);
256 Method *method = DecodeIdToMethod(decoded_data);
257 called_methods_set->insert(method);
258 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
259 data_begin += TRACE_ITEM_SIZE;
260 }
261 }
262
SaveTracingData()263 void Trace::SaveTracingData()
264 {
265 uint64_t elapsed = SystemMicroSecond() - TRACE_START_TIME;
266 PandaOStringStream ostream;
267 ostream << TRACE_STAR_CHAR << "version\n";
268 ostream << TRACE_VERSION << "\n";
269 ostream << "data-file-overflow=" << (overbrim_ ? "true" : "false") << "\n";
270 ostream << "clock=dual\n";
271 ostream << "elapsed-time-usec=" << elapsed << "\n";
272
273 size_t buf_end;
274 // Atomic with relaxed order reason: data race with buffer_offset_ with no synchronization or ordering constraints
275 // imposed on other reads or writes
276 buf_end = buffer_offset_.load(std::memory_order_relaxed);
277 size_t num_records = (buf_end - TRACE_HEADER_LENGTH) / TRACE_ITEM_SIZE;
278 ostream << "num-method-calls=" << num_records << "\n";
279 ostream << "clock-call-overhead-nsec=" << GetAverageTime() << "\n";
280 ostream << "pid=" << getpid() << "\n";
281 ostream << "vm=panda\n";
282
283 ostream << TRACE_STAR_CHAR << "threads\n";
284 RecordThreadsInfo(&ostream);
285 ostream << TRACE_STAR_CHAR << "methods\n";
286
287 PandaSet<Method *> called_methods_set;
288 GetUniqueMethods(buf_end, &called_methods_set);
289
290 RecordMethodsInfo(&ostream, called_methods_set);
291 ostream << TRACE_STAR_CHAR << "end\n";
292 PandaString methods_info(ostream.str());
293
294 trace_file_->WriteAll(reinterpret_cast<const void *>(methods_info.c_str()), methods_info.length());
295 trace_file_->WriteAll(buffer_.get(), buf_end);
296 trace_file_->Close();
297 }
298
MethodEntry(ManagedThread * thread,Method * method)299 void Trace::MethodEntry(ManagedThread *thread, Method *method)
300 {
301 os::memory::LockHolder lock(thread_info_lock_);
302 uint32_t thread_time = 0;
303 uint32_t real_time = 0;
304 GetTimes(&thread_time, &real_time);
305 WriteInfoToBuf(thread, method, EventFlag::TRACE_METHOD_ENTER, thread_time, real_time);
306
307 PandaOStringStream os;
308 auto thread_name = GetThreadName(thread);
309 os << thread->GetId() << "\t" << thread_name << "\n";
310 PandaString info = os.str();
311 thread_info_set_.insert(info);
312 }
313
MethodExit(ManagedThread * thread,Method * method)314 void Trace::MethodExit(ManagedThread *thread, Method *method)
315 {
316 uint32_t thread_time = 0;
317 uint32_t real_time = 0;
318 GetTimes(&thread_time, &real_time);
319 WriteInfoToBuf(thread, method, EventFlag::TRACE_METHOD_EXIT, thread_time, real_time);
320 }
321
GetTimes(uint32_t * thread_time,uint32_t * real_time)322 void Trace::GetTimes(uint32_t *thread_time, uint32_t *real_time)
323 {
324 *thread_time = GetCpuMicroSecond() - BASE_CPU_TIME;
325 *real_time = SystemMicroSecond() - TRACE_START_TIME;
326 }
327
328 } // namespace panda
329