• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include "profiler.h"
17 
18 #include <cutils/properties.h>
19 #include <log/log.h>
20 #include <sys/stat.h>
21 
22 #include <fstream>
23 #include <list>
24 #include <mutex>
25 #include <unordered_map>
26 #include <vector>
27 
28 namespace google {
29 namespace camera_common {
30 namespace {
31 
32 #undef LOG_TAG
33 #define LOG_TAG "profiler"
34 
35 // Profiler implementatoin.
36 class ProfilerImpl : public Profiler {
37  public:
ProfilerImpl(SetPropFlag setting)38   ProfilerImpl(SetPropFlag setting) : setting_(setting){
39     object_init_time_ = CurrentTime();
40   };
41   ~ProfilerImpl();
42 
43   // Setup the name of use case the profiler is running.
44   // Argument:
45   //  usecase: the name use case of the profiler is running.
SetUseCase(std::string usecase)46   void SetUseCase(std::string usecase) override final {
47     use_case_ = std::move(usecase);
48   }
49 
50   // Set the file prefix name for dumpping the profiling file.
51   // Argument:
52   //  dump_file_prefix: file prefix name. In the current setting,
53   //    "/data/vendor/camera/" is a valid folder for camera to dump file.
54   //    A valid prefix can be "/data/vendor/camera/test_prefix_".
55   void SetDumpFilePrefix(std::string dump_file_prefix) override final;
56 
57   // Start to profile.
58   // We use start and end to choose which code snippet to be profile.
59   // The user specifies the name, and the profiler will print the name and its
60   // timing.
61   // Arguments:
62   //   name: the name of the node to be profiled.
63   //   request_id: frame requesd id.
64   void Start(const std::string name,
65              int request_id = kInvalidRequestId) override final;
66 
67   // End the profileing.
68   // Arguments:
69   //   name: the name of the node to be profiled. Should be the same in Start().
70   //   request_id: frame requesd id.
71   void End(const std::string name,
72            int request_id = kInvalidRequestId) override final;
73 
74   // Print out the profiling result in the standard output (ANDROID_LOG_ERROR).
75   virtual void PrintResult() override;
76 
77  protected:
78   // A structure to hold start time, end time, and count of profiling code
79   // snippet.
80   struct TimeSlot {
81     int64_t start;
82     int64_t end;
83     int32_t count;
TimeSlotgoogle::camera_common::__anonb6f0bfcc0111::ProfilerImpl::TimeSlot84     TimeSlot() : start(0), end(0), count(0) {
85     }
86   };
87 
88   // A structure to store node's profiling result.
89   struct TimeResult {
90     std::string node_name;
91     float max_dt;
92     float avg_dt;
93     float avg_count;
TimeResultgoogle::camera_common::__anonb6f0bfcc0111::ProfilerImpl::TimeResult94     TimeResult(std::string node_name, float max_dt, float avg_dt, float count)
95         : node_name(node_name),
96           max_dt(max_dt),
97           avg_dt(avg_dt),
98           avg_count(count) {
99     }
100   };
101 
102   using TimeSeries = std::vector<TimeSlot>;
103   using NodeTimingMap = std::unordered_map<std::string, TimeSeries>;
104 
105   static constexpr int64_t kNsPerSec = 1000000000;
106   static constexpr float kNanoToMilli = 0.000001f;
107 
108   // The setting_ is used to memorize the getprop result.
109   SetPropFlag setting_;
110   // The map to record the timing of all nodes.
111   NodeTimingMap timing_map_;
112   // Use case name.
113   std::string use_case_;
114   // The prefix for the dump filename.
115   std::string dump_file_prefix_;
116   // Mutex lock.
117   std::mutex lock_;
118 
119   // Get boot time.
CurrentTime() const120   int64_t CurrentTime() const {
121     if (timespec now; clock_gettime(CLOCK_BOOTTIME, &now) == 0) {
122       return now.tv_sec * kNsPerSec + now.tv_nsec;
123     } else {
124       ALOGE("clock_gettime failed");
125       return -1;
126     }
127   }
128 
129   // Timestamp of the class object initialized.
130   int64_t object_init_time_;
131 
132   // Create folder if not exist.
133   void CreateFolder(std::string folder_path);
134 
135   // Dump the result to the disk.
136   // Argument:
137   //   filepath: file path to dump file.
138   void DumpResult(std::string filepath);
139 };
140 
~ProfilerImpl()141 ProfilerImpl::~ProfilerImpl() {
142   if (setting_ == SetPropFlag::kDisable || timing_map_.size() == 0) {
143     return;
144   }
145   if (setting_ & SetPropFlag::kPrintBit) {
146     PrintResult();
147   }
148   if (setting_ & SetPropFlag::kDumpBit) {
149     DumpResult(dump_file_prefix_ + use_case_ + "-TS" +
150         std::to_string(object_init_time_) + ".txt");
151   }
152 }
153 
CreateFolder(std::string folder_path)154 void ProfilerImpl::CreateFolder(std::string folder_path) {
155   struct stat folder_stat;
156   memset(&folder_stat, 0, sizeof(folder_stat));
157   if (stat(folder_path.c_str(), &folder_stat) != 0) {
158     if (errno != ENOENT ||
159         mkdir(folder_path.c_str(), S_IRWXU | S_IRWXG | S_IRWXO) == 0) {
160       ALOGE("Failed to create %s. errno: %d", folder_path.c_str(), errno);
161     }
162   }
163 }
164 
SetDumpFilePrefix(std::string dump_file_prefix)165 void ProfilerImpl::SetDumpFilePrefix(std::string dump_file_prefix) {
166   dump_file_prefix_ = dump_file_prefix;
167   if (setting_ & SetPropFlag::kDumpBit) {
168     if (auto index = dump_file_prefix_.rfind('/'); index != std::string::npos) {
169       CreateFolder(dump_file_prefix_.substr(0, index));
170     }
171   }
172 }
173 
Start(const std::string name,int request_id)174 void ProfilerImpl::Start(const std::string name, int request_id) {
175   if (setting_ == SetPropFlag::kDisable) {
176     return;
177   }
178   int index = (request_id == kInvalidRequestId ? 0 : request_id);
179 
180   std::lock_guard<std::mutex> lk(lock_);
181   TimeSeries& time_series = timing_map_[name];
182   for (int i = time_series.size(); i <= index; i++) {
183     time_series.push_back(TimeSlot());
184   }
185   TimeSlot& slot = time_series[index];
186   slot.start += CurrentTime();
187 }
188 
End(const std::string name,int request_id)189 void ProfilerImpl::End(const std::string name, int request_id) {
190   if (setting_ == SetPropFlag::kDisable) {
191     return;
192   }
193   int index = (request_id == kInvalidRequestId ? 0 : request_id);
194 
195   std::lock_guard<std::mutex> lk(lock_);
196 
197   if (index < timing_map_[name].size()) {
198     TimeSlot& slot = timing_map_[name][index];
199     slot.end += CurrentTime();
200     slot.count++;
201   }
202 }
203 
PrintResult()204 void ProfilerImpl::PrintResult() {
205   ALOGE("UseCase: %s. Profiled Frames: %d.", use_case_.c_str(),
206         static_cast<int>(timing_map_.begin()->second.size()));
207 
208   std::vector<TimeResult> time_results;
209 
210   float sum_avg = 0.f;
211   float max_max = 0.f;
212   float sum_max = 0.f;
213   for (const auto& [node_name, time_series] : timing_map_) {
214     int num_frames = 0;
215     int num_samples = 0;
216     float sum_dt = 0;
217     float max_dt = 0;
218     for (const auto& slot : time_series) {
219       if (slot.count > 0) {
220         float elapsed = (slot.end - slot.start) * kNanoToMilli;
221         sum_dt += elapsed;
222         num_samples += slot.count;
223         max_dt = std::max(max_dt, elapsed);
224         num_frames++;
225       }
226     }
227     if (num_samples == 0) {
228       continue;
229     }
230     float avg = sum_dt / std::max(1, num_samples);
231     float avg_count = static_cast<float>(num_samples) /
232                       static_cast<float>(std::max(1, num_frames));
233     sum_avg += avg * avg_count;
234     sum_max += max_dt;
235     max_max = std::max(max_max, max_dt);
236 
237     time_results.push_back({node_name, max_dt, avg * avg_count, avg_count});
238   }
239 
240   std::sort(time_results.begin(), time_results.end(),
241             [](auto a, auto b) { return a.avg_dt > b.avg_dt; });
242 
243   for (const auto it : time_results) {
244     ALOGE("%51.51s Max: %8.3f ms       Avg: %7.3f ms (Count = %3.1f)",
245           it.node_name.c_str(), it.max_dt, it.avg_dt, it.avg_count);
246   }
247 
248   ALOGE("%43.43s     MAX SUM: %8.3f ms,  AVG SUM: %7.3f ms", "", sum_max,
249         sum_avg);
250   ALOGE("");
251 }
252 
DumpResult(std::string filepath)253 void ProfilerImpl::DumpResult(std::string filepath) {
254   if (std::ofstream fout(filepath, std::ios::out); fout.is_open()) {
255     for (const auto& [node_name, time_series] : timing_map_) {
256       fout << node_name << " ";
257       for (const auto& time_slot : time_series) {
258         float elapsed = static_cast<float>(time_slot.end - time_slot.start) /
259                         std::max(1, time_slot.count);
260         fout << elapsed * kNanoToMilli << " ";
261       }
262       fout << "\n";
263     }
264     fout.close();
265   }
266 }
267 
268 class ProfilerStopwatchImpl : public ProfilerImpl {
269  public:
ProfilerStopwatchImpl(SetPropFlag setting)270   ProfilerStopwatchImpl(SetPropFlag setting) : ProfilerImpl(setting){};
271 
~ProfilerStopwatchImpl()272   ~ProfilerStopwatchImpl() {
273     if (setting_ == SetPropFlag::kDisable || timing_map_.size() == 0) {
274       return;
275     }
276     if (setting_ & SetPropFlag::kPrintBit) {
277       // Virtual function won't work in parent class's destructor. need to call
278       // it by ourself.
279       PrintResult();
280       // Erase the print bit to prevent parent class print again.
281       setting_ = static_cast<SetPropFlag>(setting_ & (!SetPropFlag::kPrintBit));
282     }
283   }
284 
285   // Print out the profiling result in the standard output (ANDROID_LOG_ERROR)
286   // with stopwatch mode.
PrintResult()287   void PrintResult() override {
288     ALOGE("Profiling Case: %s", use_case_.c_str());
289 
290     // Sort by end time.
291     std::list<std::pair<std::string, TimeSlot>> time_results;
292     for (const auto& [node_name, time_series] : timing_map_) {
293       for (const auto& slot : time_series) {
294         if (slot.count > 0 && time_results.size() < time_results.max_size()) {
295           time_results.push_back({node_name, slot});
296         }
297       }
298     }
299     time_results.sort([](const auto& a, const auto& b) {
300       return a.second.end < b.second.end;
301     });
302 
303     for (const auto& [node_name, slot] : time_results) {
304       if (slot.count > 0) {
305         float elapsed = (slot.end - slot.start) * kNanoToMilli;
306         ALOGE("%51.51s: %8.3f ms", node_name.c_str(), elapsed);
307       }
308     }
309 
310     ALOGE("");
311   }
312 };
313 
314 // Dummpy profiler class.
315 class ProfilerDummy : public Profiler {
316  public:
ProfilerDummy()317   ProfilerDummy(){};
~ProfilerDummy()318   ~ProfilerDummy(){};
319 
SetUseCase(std::string)320   void SetUseCase(std::string) override final {
321   }
322 
SetDumpFilePrefix(std::string)323   void SetDumpFilePrefix(std::string) override final {
324   }
325 
Start(const std::string,int)326   void Start(const std::string, int) override final {
327   }
328 
End(const std::string,int)329   void End(const std::string, int) override final {
330   }
331 
PrintResult()332   void PrintResult() override final {
333   }
334 };
335 
336 }  // anonymous namespace
337 
Create(int option)338 std::shared_ptr<Profiler> Profiler::Create(int option) {
339   SetPropFlag flag = static_cast<SetPropFlag>(option);
340 
341   if (flag == SetPropFlag::kDisable) {
342     return std::make_unique<ProfilerDummy>();
343   } else if (flag & SetPropFlag::kStopWatch) {
344     return std::make_unique<ProfilerStopwatchImpl>(flag);
345   } else {
346     return std::make_unique<ProfilerImpl>(flag);
347   }
348 }
349 
350 }  // namespace camera_common
351 }  // namespace google
352