• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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, &current);
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, &current);
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, &current);
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