• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2024 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 #ifndef ART_RUNTIME_TRACE_PROFILE_H_
18 #define ART_RUNTIME_TRACE_PROFILE_H_
19 
20 #include <unordered_set>
21 
22 #include "base/locks.h"
23 #include "base/macros.h"
24 #include "base/os.h"
25 #include "thread.h"
26 #include "thread_pool.h"
27 
28 namespace art HIDDEN {
29 
30 class ArtMethod;
31 
32 // TODO(mythria): A randomly chosen value. Tune it later based on the number of
33 // entries required in the buffer.
34 static constexpr size_t kAlwaysOnTraceBufSize = 2048;
35 
36 // The typical frequency at which the timestamp counters are updated is 24576000.
37 // 2^23 (8388608) corresponds to about 341ms at that frequency.
38 static constexpr size_t kLongRunningMethodThreshold = 1 << 23;
39 
40 enum class LowOverheadTraceType {
41   kLongRunningMethods,
42   kAllMethods,
43   kNone
44 };
45 
46 class TraceData {
47  public:
TraceData(LowOverheadTraceType trace_type)48   explicit TraceData(LowOverheadTraceType trace_type)
49       : curr_buffer_(nullptr),
50         curr_index_(0),
51         trace_type_(trace_type),
52         trace_end_time_(0),
53         trace_dump_in_progress_(false),
54         trace_dump_condition_("trace dump condition", *Locks::trace_lock_),
55         trace_data_lock_("Trace Data lock", LockLevel::kGenericBottomLock) {}
56 
GetTraceType()57   LowOverheadTraceType GetTraceType() const {
58     return trace_type_;
59   }
60 
GetTraceEndTime()61   uint64_t GetTraceEndTime() const {
62     return trace_end_time_;
63   }
64 
SetTraceEndTime(uint64_t end_time)65   void SetTraceEndTime(uint64_t end_time) {
66     trace_end_time_ = end_time;
67   }
68 
69   // Dumps events collected in the buffers and the information about threads and methods into the
70   // output stream.
71   void DumpData(std::ostringstream& os);
72 
73   void AppendToLongRunningMethods(const uint8_t* buffer, size_t size);
74 
AddTracedMethods(std::unordered_set<ArtMethod * > & methods)75   void AddTracedMethods(std::unordered_set<ArtMethod*>& methods) {
76     MutexLock mu(Thread::Current(), trace_data_lock_);
77     traced_methods_.merge(methods);
78   }
79 
AddTracedMethod(ArtMethod * method)80   void AddTracedMethod(ArtMethod* method) {
81     MutexLock mu(Thread::Current(), trace_data_lock_);
82     traced_methods_.insert(method);
83   }
84 
85   void AddTracedThread(Thread* thread);
86 
87   // If there is no trace dump in progress this returns immediately. Otherwise
88   // it waits on a condition variable waiting for the trace dump to finish.
89   void MaybeWaitForTraceDumpToFinish() REQUIRES(Locks::trace_lock_);
90 
91   // Called when a trace dump is finished to notify any waiting requests. This
92   // also resets the trace_dump_in_progress_ to false.
93   void SignalTraceDumpComplete() REQUIRES(Locks::trace_lock_);
94 
SetTraceDumpInProgress()95   void SetTraceDumpInProgress() REQUIRES(Locks::trace_lock_) {
96     trace_dump_in_progress_ = true;
97   }
98 
IsTraceDumpInProgress()99   bool IsTraceDumpInProgress() const REQUIRES(Locks::trace_lock_) {
100     return trace_dump_in_progress_;
101   }
102 
103  private:
104   // This is used to hold the long running methods when the per-thread buffer overflows.
105   std::unique_ptr<uint8_t> curr_buffer_ GUARDED_BY(trace_data_lock_);
106 
107   // The index of the next free space in the curr_buffer_
108   size_t curr_index_ GUARDED_BY(trace_data_lock_);
109 
110   // When the curr_buffer_ becomes full, we store it in this list and allocate a new buffer.
111   std::vector<std::unique_ptr<uint8_t>> overflow_buffers_ GUARDED_BY(trace_data_lock_);
112 
113   LowOverheadTraceType trace_type_;
114 
115   uint64_t trace_end_time_;
116 
117   // These hold the methods and threads see so far. These are used to generate information about
118   // the methods and threads.
119   std::unordered_set<ArtMethod*> traced_methods_ GUARDED_BY(trace_data_lock_);
120 
121   // Threads might exit before we dump the data, so record thread id and name when we see a new
122   // thread.
123   std::unordered_map<size_t, std::string> traced_threads_ GUARDED_BY(trace_data_lock_);
124 
125   // This specifies if a trace dump is in progress. We release the trace_lock_
126   // when waiting for the checkpoints to finish. We shouldn't delete trace data
127   // when a dump is in progress. trace_dump_in_progress_ and
128   // trace_dump_condition_ are used to make sure we wait for any in progress
129   // trace dumps to finish before deleting the trace data.
130   bool trace_dump_in_progress_ GUARDED_BY(Locks::trace_lock_);
131   ConditionVariable trace_dump_condition_ GUARDED_BY(Locks::trace_lock_);
132 
133   // Lock to synchronize access to traced_methods_, traced_threads_ and curr_buffer_ which
134   // can be accessed simultaneously by multiple threads when running TraceDumpCheckpoint.
135   Mutex trace_data_lock_;
136 };
137 
138 class TraceDumpCheckpoint final : public Closure {
139  public:
TraceDumpCheckpoint(TraceData * trace_data,const std::unique_ptr<File> & trace_file)140   TraceDumpCheckpoint(TraceData* trace_data, const std::unique_ptr<File>& trace_file)
141       : barrier_(0),
142         trace_data_(trace_data),
143         trace_file_(trace_file),
144         trace_file_lock_("trace file lock", LockLevel::kGenericBottomLock) {}
145 
146   void Run(Thread* thread) override REQUIRES_SHARED(Locks::mutator_lock_);
147   void WaitForThreadsToRunThroughCheckpoint(size_t threads_running_checkpoint);
148   void FinishTraceDump(std::ostringstream& os);
149 
150  private:
151   // The barrier to be passed through and for the requestor to wait upon.
152   Barrier barrier_;
153 
154   // Trace data to record the data from each thread.
155   TraceData* trace_data_;
156 
157   // Trace file to flush the data. If the trace_file_ is empty then the data is recorded in the
158   // trace_data_.
159   const std::unique_ptr<File>& trace_file_ GUARDED_BY(trace_file_lock_);
160 
161   // Lock to synchronize access to trace_file_. We need to write the data of
162   // each thread as a block so we hold a lock while flushing the data.
163   Mutex trace_file_lock_;
164 };
165 
166 // This class implements low-overhead tracing. This feature is available only when
167 // always_enable_profile_code is enabled which is a build time flag defined in
168 // build/flags/art-flags.aconfig. When this flag is enabled, AOT and JITed code can record events
169 // on each method execution. When a profile is started, method entry / exit events are recorded in
170 // a per-thread circular buffer. When requested the recorded events in the buffer are dumped into a
171 // file. The buffers are released when the profile is stopped.
172 class TraceProfiler {
173  public:
174   // Starts profiling by allocating a per-thread buffer for all the threads.
175   static void Start();
176 
177   // Starts recording long running methods. A long running method means any
178   // method that executes for more than kLongRunningMethodDuration.
179   static void StartTraceLongRunningMethods(uint64_t trace_duration_ns);
180 
181   // Releases all the buffers.
182   static void Stop();
183 
184   // Dumps the recorded events in the buffer from all threads in the specified file.
185   static void Dump(int fd);
186   static void Dump(const char* trace_filename);
187 
188   // Get the long running methods as a string. This is used in the sigquit handler to record
189   // information about long running methods.
190   static std::string GetLongRunningMethodsString();
191 
192   // Called when thread is exiting to release the allocated buffer.
193   static void ReleaseThreadBuffer(Thread* self) REQUIRES(Locks::trace_lock_);
194 
195   static bool IsTraceProfileInProgress() REQUIRES(Locks::trace_lock_);
196 
197   // Allocates a buffer for the specified thread.
198   static void AllocateBuffer(Thread* thread);
199 
200   // Used to flush the long running method buffer when it is full. This method flushes all methods
201   // that have already seen an exit and records them into a string. If we don't have sufficient free
202   // entries after this processing (for example: if we have a really deep call stack) then we record
203   // a placeholder method exit event and flush all events.
204   static void FlushBufferAndRecordTraceEvent(ArtMethod* method, Thread* thread, bool is_entry);
205 
206   static LowOverheadTraceType GetTraceType();
207 
208   // Callback that is run when the specified duration for the long running trace has elapsed. If the
209   // trace is still running then tracing is stopped and all buffers are released. If the trace
210   // has already stopped then this request is ignored.
211   static void TraceTimeElapsed();
212 
213  private:
214   // Starts tracing.
215   static void Start(LowOverheadTraceType trace_type, uint64_t trace_duration_ns);
216 
217   // Dumps the tracing data into the specified trace_file
218   static void Dump(std::unique_ptr<File>&& trace_file, std::ostringstream& os);
219 
220   // Stops tracing.
221   static void StopLocked() REQUIRES(Locks::trace_lock_);
222 
223   // This method goes over all the events in the thread_buffer and stores the encoded event in the
224   // buffer. It returns the number of bytes written into the buffer.
225   // This also records the ArtMethods from the events in the thread_buffer in a set. This set is
226   // used to dump the information about the methods once buffers from all threads have been
227   // processed.
228   static size_t DumpBuffer(uint32_t thread_id,
229                            uintptr_t* thread_buffer,
230                            uint8_t* buffer /* out */,
231                            std::unordered_set<ArtMethod*>& methods /* out */);
232 
233   // Dumps all the trace events from the thread into the buffer. Also records the ArtMethods from
234   // the events which is then used to record information about these methods.
235   static size_t DumpLongRunningMethodBuffer(uint32_t thread_id,
236                                             uintptr_t* method_trace_entries,
237                                             uintptr_t* end_trace_entries,
238                                             uint8_t* buffer,
239                                             std::unordered_set<ArtMethod*>& methods);
240 
241   static bool profile_in_progress_ GUARDED_BY(Locks::trace_lock_);
242 
243   static TraceData* trace_data_ GUARDED_BY(Locks::trace_lock_);
244 
245   friend class TraceDumpCheckpoint;
246   DISALLOW_COPY_AND_ASSIGN(TraceProfiler);
247 };
248 
249 }  // namespace art
250 
251 #endif  // ART_RUNTIME_TRACE_PROFILE_H_
252