• 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 <hardware/google/camera/common/profiler/profiler.pb.h>
20 #include <log/log.h>
21 #include <sys/stat.h>
22 
23 #include <fstream>
24 #include <list>
25 #include <mutex>
26 #include <unordered_map>
27 #include <vector>
28 
29 namespace google {
30 namespace camera_common {
31 namespace {
32 
33 #undef LOG_TAG
34 #define LOG_TAG "profiler"
35 
StandardDeviation(std::vector<float> samples,float mean)36 float StandardDeviation(std::vector<float> samples, float mean) {
37   int size = samples.size();
38 
39   double sum = 0;
40   for (int i = 0; i < size; i++) {
41     sum += pow((samples[i] - mean), 2);
42   }
43 
44   return static_cast<float>(sqrt(sum / (size - 1)));
45 }
46 
47 // Profiler implementatoin.
48 class ProfilerImpl : public Profiler {
49  public:
ProfilerImpl(SetPropFlag setting)50   ProfilerImpl(SetPropFlag setting) : setting_(setting) {
51     object_init_real_time_ = GetRealTimeNs();
52     object_init_boot_time_ = GetBootTimeNs();
53   };
54   ~ProfilerImpl();
55 
56   // Setup the name of use case the profiler is running.
57   // Argument:
58   //  usecase: the name use case of the profiler is running.
SetUseCase(std::string usecase)59   void SetUseCase(std::string usecase) override final {
60     use_case_ = std::move(usecase);
61   }
62 
63   // Set the file prefix name for dumpping the profiling file.
64   // Argument:
65   //  dump_file_prefix: file prefix name. In the current setting,
66   //    "/data/vendor/camera/" is a valid folder for camera to dump file.
67   //    A valid prefix can be "/data/vendor/camera/test_prefix_".
68   void SetDumpFilePrefix(const std::string& dump_file_prefix) override final;
69 
70   // Start to profile.
71   // We use start and end to choose which code snippet to be profile.
72   // The user specifies the name, and the profiler will print the name and its
73   // timing.
74   // Arguments:
75   //   name: the name of the node to be profiled.
76   //   request_id: frame requesd id.
77   void Start(const std::string& name,
78              int request_id = kInvalidRequestId) override final;
79 
80   // End the profileing.
81   // Arguments:
82   //   name: the name of the node to be profiled. Should be the same in Start().
83   //   request_id: frame requesd id.
84   void End(const std::string& name,
85            int request_id = kInvalidRequestId) override final;
86 
87   // Print out the profiling result in the standard output (ANDROID_LOG_ERROR).
88   void PrintResult() override;
89 
90   // Profile the frame rate
91   void ProfileFrameRate(const std::string&) override final;
92 
93   // Set the interval of FPS print
94   // The unit is second and interval_seconds must >= 1
95   void SetFpsPrintInterval(int32_t interval_seconds) override final;
96 
97   // Get the latency associated with the name
98   std::list<std::pair<std::string, float>> GetLatencyData() override final;
99 
GetUseCase() const100   std::string GetUseCase() const override final {
101     return use_case_;
102   }
103 
104  protected:
105   // A structure to hold start time, end time, and count of profiling code
106   // snippet.
107   struct TimeSlot {
108     int64_t start = 0;
109     int64_t end = 0;
110     int32_t count = 0;
111     int32_t request_id = 0;
112   };
113 
114   // A structure to store node's profiling result.
115   struct TimeResult {
116     std::string node_name;
117     float min_dt;
118     float max_dt;
119     float avg_dt;
120     float avg_count;
121     float fps;
122     float mean_max_stddevs;
TimeResultgoogle::camera_common::__anonca3bec460111::ProfilerImpl::TimeResult123     TimeResult(std::string node_name, float min_dt, float max_dt, float avg_dt,
124                float count, float fps, float mean_max_stddevs)
125         : node_name(node_name),
126           min_dt(min_dt),
127           max_dt(max_dt),
128           avg_dt(avg_dt),
129           avg_count(count),
130           fps(fps),
131           mean_max_stddevs(mean_max_stddevs) {
132     }
133   };
134 
135   using TimeSeries = std::vector<TimeSlot>;
136   using NodeTimingMap = std::unordered_map<std::string, TimeSeries>;
137   using NodeFrameRateMap = std::unordered_map<std::string, TimeSlot>;
138 
139   static constexpr int64_t kNsPerSec = 1000000000;
140   static constexpr float kNanoToMilli = 0.000001f;
141 
142   // The setting_ is used to memorize the getprop result.
143   SetPropFlag setting_;
144   // The map to record the timing of all nodes.
145   NodeTimingMap timing_map_;
146   // The map to record the timing to print fps when close.
147   NodeFrameRateMap frame_rate_map_;
148   // The map to record the timing to print fps per second.
149   NodeFrameRateMap realtime_frame_rate_map_;
150   // Use case name.
151   std::string use_case_;
152   // The prefix for the dump filename.
153   std::string dump_file_prefix_;
154   // Mutex lock.
155   std::mutex lock_;
156 
157   // Get clock boot time.
GetBootTimeNs() const158   int64_t GetBootTimeNs() const {
159     if (timespec now; clock_gettime(CLOCK_BOOTTIME, &now) == 0) {
160       return now.tv_sec * kNsPerSec + now.tv_nsec;
161     } else {
162       ALOGE("clock_gettime failed");
163       return -1;
164     }
165   }
166   // Get clock real time.
GetRealTimeNs() const167   int64_t GetRealTimeNs() const {
168     if (timespec now; clock_gettime(CLOCK_REALTIME, &now) == 0) {
169       return now.tv_sec * kNsPerSec + now.tv_nsec;
170     } else {
171       ALOGE("clock_gettime failed");
172       return -1;
173     }
174   }
175 
176   // Timestamp of the class object initialized using CLOCK_BOOTTIME.
177   int64_t object_init_boot_time_;
178   // Timestamp of the class object initialized using CLOCK_REALTIME.
179   int64_t object_init_real_time_;
180 
181   // Create folder if not exist.
182   void CreateFolder(const std::string& folder_path);
183 
184   // Dump the result to the disk.
185   // Argument:
186   //   filepath: file path to dump file.
187   virtual void DumpResult(const std::string& filepath);
188 
189   // Dump result in text format.
190   void DumpTxt(std::string_view filepath);
191 
192   // Dump result in proto binary format.
193   void DumpPb(std::string_view filepath);
194 
195   // Dump result format extension: proto or text.
196   constexpr static char kStrPb[] = ".pb";
197   constexpr static char kStrTxt[] = ".txt";
198 
199   int32_t fps_print_interval_seconds_ = 1;
200 };
201 
~ProfilerImpl()202 ProfilerImpl::~ProfilerImpl() {
203   if (setting_ == SetPropFlag::kDisable || timing_map_.size() == 0) {
204     return;
205   }
206   if (setting_ & SetPropFlag::kPrintBit) {
207     PrintResult();
208   }
209   if (setting_ & SetPropFlag::kDumpBit) {
210     std::string filename = std::to_string(object_init_real_time_);
211     DumpResult(dump_file_prefix_ + use_case_ + "-TS" + filename);
212   }
213 }
214 
DumpResult(const std::string & filepath)215 void ProfilerImpl::DumpResult(const std::string& filepath) {
216   if (setting_ & SetPropFlag::kProto) {
217     DumpPb(filepath + kStrPb);
218   } else {
219     DumpTxt(filepath + kStrTxt);
220   }
221 }
222 
CreateFolder(const std::string & folder_path)223 void ProfilerImpl::CreateFolder(const std::string& folder_path) {
224   struct stat folder_stat;
225   memset(&folder_stat, 0, sizeof(folder_stat));
226   if (stat(folder_path.c_str(), &folder_stat) != 0) {
227     if (errno != ENOENT ||
228         mkdir(folder_path.c_str(), S_IRWXU | S_IRWXG | S_IRWXO) == 0) {
229       ALOGE("Failed to create %s. errno: %d", folder_path.c_str(), errno);
230     }
231   }
232 }
233 
SetDumpFilePrefix(const std::string & dump_file_prefix)234 void ProfilerImpl::SetDumpFilePrefix(const std::string& dump_file_prefix) {
235   dump_file_prefix_ = dump_file_prefix;
236   if (setting_ & SetPropFlag::kDumpBit) {
237     if (auto index = dump_file_prefix_.rfind('/'); index != std::string::npos) {
238       CreateFolder(dump_file_prefix_.substr(0, index));
239     }
240   }
241 }
242 
SetFpsPrintInterval(int32_t interval_seconds)243 void ProfilerImpl::SetFpsPrintInterval(int32_t interval_seconds) {
244   if (interval_seconds < 1) {
245     ALOGE("Wrong interval: %d, must >= 1", interval_seconds);
246     return;
247   }
248   fps_print_interval_seconds_ = interval_seconds;
249 }
250 
ProfileFrameRate(const std::string & name)251 void ProfilerImpl::ProfileFrameRate(const std::string& name) {
252   std::lock_guard<std::mutex> lk(lock_);
253   // Save the timeing for each whole process
254   TimeSlot& frame_rate = frame_rate_map_[name];
255   if (frame_rate.start == 0) {
256     frame_rate.start = GetBootTimeNs();
257     frame_rate.count = 0;
258     frame_rate.end = 0;
259   } else {
260     ++frame_rate.count;
261     frame_rate.end = GetBootTimeNs();
262   }
263 
264   if ((setting_ & SetPropFlag::kPrintFpsPerIntervalBit) == 0) {
265     return;
266   }
267   // Print FPS every second
268   TimeSlot& realtime_frame_rate = realtime_frame_rate_map_[name];
269   if (realtime_frame_rate.start == 0) {
270     realtime_frame_rate.start = GetBootTimeNs();
271     realtime_frame_rate.count = 0;
272   } else {
273     ++realtime_frame_rate.count;
274     int64_t current = GetBootTimeNs();
275     int64_t elapsed = current - realtime_frame_rate.start;
276     if (elapsed > kNsPerSec * fps_print_interval_seconds_) {
277       float fps =
278           realtime_frame_rate.count * kNsPerSec / static_cast<float>(elapsed);
279       float avg_fps = frame_rate.count * kNsPerSec /
280                       static_cast<float>(frame_rate.end - frame_rate.start);
281       ALOGI("%s: current FPS %3.2f, avg %3.2f", name.c_str(), fps, avg_fps);
282       realtime_frame_rate.count = 0;
283       realtime_frame_rate.start = current;
284     }
285   }
286 }
287 
Start(const std::string & name,int request_id)288 void ProfilerImpl::Start(const std::string& name, int request_id) {
289   if (setting_ == SetPropFlag::kDisable) {
290     return;
291   }
292 
293   // When the request_id == kInvalidRequestId, it is served as a different
294   // purpose, eg. profiling first frame latency, or HAL total runtime. The valid
295   // request id is shifted by 1 to avoid the conflict.
296   int valid_request_id = (request_id == kInvalidRequestId) ? 0 : request_id + 1;
297 
298   {
299     std::lock_guard<std::mutex> lk(lock_);
300     TimeSeries& time_series = timing_map_[name];
301     for (int i = time_series.size(); i <= valid_request_id; ++i) {
302       time_series.push_back(TimeSlot());
303     }
304     TimeSlot& slot = time_series[valid_request_id];
305     slot.request_id = valid_request_id;
306     slot.start += GetBootTimeNs();
307   }
308 
309   if ((setting_ & SetPropFlag::kCalculateFpsOnEndBit) == 0) {
310     ProfileFrameRate(name);
311   }
312 }
313 
End(const std::string & name,int request_id)314 void ProfilerImpl::End(const std::string& name, int request_id) {
315   if (setting_ == SetPropFlag::kDisable) {
316     return;
317   }
318 
319   // When the request_id == kInvalidRequestId, it is served as a different
320   // purpose, eg. profiling first frame latency, or HAL total runtime. The valid
321   // request id is shifted by 1 to avoid the conflict.
322   int valid_request_id = (request_id == kInvalidRequestId) ? 0 : request_id + 1;
323 
324   {
325     std::lock_guard<std::mutex> lk(lock_);
326     if (static_cast<std::size_t>(valid_request_id) < timing_map_[name].size()) {
327       TimeSlot& slot = timing_map_[name][valid_request_id];
328       slot.end += GetBootTimeNs();
329       ++slot.count;
330     }
331   }
332 
333   if ((setting_ & SetPropFlag::kCalculateFpsOnEndBit) != 0) {
334     ProfileFrameRate(name);
335   }
336 }
337 
PrintResult()338 void ProfilerImpl::PrintResult() {
339   ALOGI("UseCase: %s. Profiled Frames: %d.", use_case_.c_str(),
340         static_cast<int>(timing_map_.begin()->second.size()));
341 
342   std::vector<TimeResult> time_results;
343 
344   float sum_avg = 0.f;
345   float max_max = 0.f;
346   float sum_min = 0.f;
347   float sum_max = 0.f;
348   for (const auto& [node_name, time_series] : timing_map_) {
349     int num_frames = 0;
350     int num_samples = 0;
351     float sum_dt = 0.f;
352     float min_dt = std::numeric_limits<float>::max();
353     float max_dt = 0.f;
354     float mean_dt = 0.f;
355     std::vector<float> elapses;
356     for (const auto& slot : time_series) {
357       if (slot.count > 0) {
358         float elapsed = (slot.end - slot.start) * kNanoToMilli;
359         sum_dt += elapsed;
360         num_samples += slot.count;
361         min_dt = std::min(min_dt, elapsed);
362         max_dt = std::max(max_dt, elapsed);
363         num_frames++;
364         elapses.push_back(elapsed);
365       }
366     }
367     if (num_samples == 0) {
368       continue;
369     }
370     float avg = sum_dt / std::max(1, num_samples);
371     float avg_count = static_cast<float>(num_samples) /
372                       static_cast<float>(std::max(1, num_frames));
373     mean_dt = avg * avg_count;
374     sum_avg += mean_dt;
375     sum_min += min_dt;
376     sum_max += max_dt;
377     max_max = std::max(max_max, max_dt);
378 
379     // calculate StandardDeviation
380     float mean_max_stddevs = 0.f;
381     if (elapses.size() > 1) {
382       float dev_dt = StandardDeviation(elapses, mean_dt);
383       mean_max_stddevs = (max_dt - mean_dt) / dev_dt;
384     }
385 
386     TimeSlot& frame_rate = frame_rate_map_[node_name];
387     int64_t duration = frame_rate.end - frame_rate.start;
388     float fps = 0;
389     if (duration > kNsPerSec) {
390       fps = frame_rate.count * kNsPerSec / static_cast<float>(duration);
391     }
392     time_results.push_back(
393         {node_name, min_dt, max_dt, mean_dt, avg_count, fps, mean_max_stddevs});
394   }
395 
396   std::sort(time_results.begin(), time_results.end(),
397             [](auto a, auto b) { return a.avg_dt > b.avg_dt; });
398 
399   for (const auto& result : time_results) {
400     if (result.fps == 0) {
401       ALOGI(
402           "%51.51s Min: %8.3f ms,  Max: %8.3f ms,  Avg: %7.3f ms "
403           "(Count = %3.1f),  mean_max_stddevs: %6.2f,  fps:    NA",
404           result.node_name.c_str(), result.min_dt, result.max_dt, result.avg_dt,
405           result.avg_count, result.mean_max_stddevs);
406     } else {
407       ALOGI(
408           "%51.51s Min: %8.3f ms,  Max: %8.3f ms,  Avg: %7.3f ms "
409           "(Count = %3.1f),  mean_max_stddevs: %6.2f,  fps: %8.2f",
410           result.node_name.c_str(), result.min_dt, result.max_dt, result.avg_dt,
411           result.avg_count, result.mean_max_stddevs, result.fps);
412     }
413   }
414 
415   ALOGI("%43.43s     MIN SUM: %8.3f ms,  MAX SUM: %8.3f ms,  AVG SUM: %7.3f ms",
416         "", sum_min, sum_max, sum_avg);
417   ALOGI("");
418 }
419 
DumpTxt(std::string_view filepath)420 void ProfilerImpl::DumpTxt(std::string_view filepath) {
421   // The dump result data is organized as 3 sections:
422   //  1. detla time and fps of each frame.
423   //  2. start time of each frame.
424   //  3. end time of each frame.
425   if (std::ofstream fout(filepath, std::ios::out); fout.is_open()) {
426     fout << "// PROFILER_DELTA_TIME_AND_FPS, UNIT:MILLISECOND //\n";
427     for (const auto& [node_name, time_series] : timing_map_) {
428       fout << node_name << " ";
429       for (const auto& time_slot : time_series) {
430         float elapsed = static_cast<float>(time_slot.end - time_slot.start) /
431                         std::max(1, time_slot.count);
432         fout << elapsed * kNanoToMilli << " ";
433       }
434       fout << "\n";
435       TimeSlot& frame_rate = frame_rate_map_[node_name];
436       int64_t duration = frame_rate.end - frame_rate.start;
437       float fps = 0;
438       if (duration > kNsPerSec) {
439         fps = frame_rate.count * kNsPerSec / static_cast<float>(duration);
440       }
441       if (fps > 0) {
442         fout << node_name << " fps:" << fps;
443       } else {
444         fout << node_name << " fps: NA";
445       }
446       fout << "\n";
447     }
448 
449     fout << "\n// PROFILER_START_TIME, AVG TIMESTAMP, UNIT:NANOSECOND //\n";
450     for (const auto& [node_name, time_series] : timing_map_) {
451       fout << node_name << " ";
452       for (const auto& time_slot : time_series) {
453         int64_t avg_time_stamp = time_slot.start / std::max(1, time_slot.count);
454         fout << avg_time_stamp << " ";
455       }
456       fout << "\n";
457     }
458 
459     fout << "\n// PROFILER_END_TIME, AVG TIMESTAMP,  UNIT:NANOSECOND //\n";
460     for (const auto& [node_name, time_series] : timing_map_) {
461       fout << node_name << " ";
462       for (const auto& time_slot : time_series) {
463         int64_t avg_time_stamp = time_slot.end / std::max(1, time_slot.count);
464         fout << avg_time_stamp << " ";
465       }
466       fout << "\n";
467     }
468     fout.close();
469   }
470 }
471 
DumpPb(std::string_view filepath)472 void ProfilerImpl::DumpPb(std::string_view filepath) {
473   if (std::ofstream fout(filepath, std::ios::out); fout.is_open()) {
474     profiler::ProfilingResult profiling_result;
475     profiling_result.set_usecase(use_case_);
476     profiling_result.set_profile_start_time_nanos(object_init_real_time_);
477     profiling_result.set_profile_start_boottime_nanos(object_init_boot_time_);
478     profiling_result.set_profile_end_time_nanos(GetRealTimeNs());
479 
480     for (const auto& [node_name, time_series] : timing_map_) {
481       profiler::TimeSeries& target = *profiling_result.add_target();
482       target.set_name(node_name);
483       for (const auto& time_slot : time_series) {
484         profiler::TimeStamp& time_stamp = *target.add_runtime();
485         // A single node can be called multiple times in a frame. Every time the
486         // node is called in the same frame, the profiler accumulates the
487         // timestamp value in time_slot.start/end, and increments the count.
488         // Therefore the result timestamp we stored is the `average` timestamp.
489         // Note: consider using minimum-start, and maximum-end.
490         time_stamp.set_start(time_slot.start / std::max(1, time_slot.count));
491         time_stamp.set_end(time_slot.end / std::max(1, time_slot.count));
492         time_stamp.set_count(time_slot.count);
493         time_stamp.set_request_id(time_slot.request_id);
494       }
495     }
496     profiling_result.SerializeToOstream(&fout);
497     fout.close();
498   }
499 }
500 
501 // Get the latency associated with the name
GetLatencyData()502 std::list<std::pair<std::string, float>> ProfilerImpl::GetLatencyData() {
503   std::list<std::pair<std::string, TimeSlot>> time_results;
504   std::list<std::pair<std::string, float>> latency_data;
505   for (const auto& [node_name, time_series] : timing_map_) {
506     for (const auto& slot : time_series) {
507       if (slot.count > 0 && time_results.size() < time_results.max_size()) {
508         time_results.push_back({node_name, slot});
509       }
510     }
511   }
512   time_results.sort(
513       [](const auto& a, const auto& b) { return a.second.end < b.second.end; });
514 
515   for (const auto& [node_name, slot] : time_results) {
516     if (slot.count > 0) {
517       float elapsed = (slot.end - slot.start) * kNanoToMilli;
518       latency_data.push_back({node_name, elapsed});
519     }
520   }
521   return latency_data;
522 }
523 
524 class ProfilerStopwatchImpl : public ProfilerImpl {
525  public:
ProfilerStopwatchImpl(SetPropFlag setting)526   ProfilerStopwatchImpl(SetPropFlag setting) : ProfilerImpl(setting){};
527 
~ProfilerStopwatchImpl()528   ~ProfilerStopwatchImpl() {
529     if (setting_ == SetPropFlag::kDisable || timing_map_.size() == 0) {
530       return;
531     }
532     if (setting_ & SetPropFlag::kPrintBit) {
533       // Virtual function won't work in parent class's destructor. need to
534       // call it by ourself.
535       PrintResult();
536       // Erase the print bit to prevent parent class print again.
537       setting_ = static_cast<SetPropFlag>(setting_ & (~SetPropFlag::kPrintBit));
538     }
539     if (setting_ & SetPropFlag::kDumpBit) {
540       DumpResult(dump_file_prefix_ + use_case_ + "-TS" +
541                  std::to_string(object_init_real_time_) + ".txt");
542       setting_ = static_cast<SetPropFlag>(setting_ & (~SetPropFlag::kDumpBit));
543     }
544   }
545 
546   // Print out the profiling result in the standard output (ANDROID_LOG_ERROR)
547   // with stopwatch mode.
PrintResult()548   void PrintResult() override {
549     ALOGI("Profiling Case: %s", use_case_.c_str());
550 
551     // Sort by end time.
552     std::list<std::pair<std::string, TimeSlot>> time_results;
553     for (const auto& [node_name, time_series] : timing_map_) {
554       for (const auto& slot : time_series) {
555         if (slot.count > 0 && time_results.size() < time_results.max_size()) {
556           time_results.push_back({node_name, slot});
557         }
558       }
559     }
560     time_results.sort([](const auto& a, const auto& b) {
561       return a.second.end < b.second.end;
562     });
563 
564     for (const auto& [node_name, slot] : time_results) {
565       if (slot.count > 0) {
566         float elapsed = (slot.end - slot.start) * kNanoToMilli;
567         ALOGI("%51.51s: %8.3f ms", node_name.c_str(), elapsed);
568       }
569     }
570 
571     ALOGI("");
572   }
573 
DumpResult(const std::string & filepath)574   void DumpResult(const std::string& filepath) override {
575     if (std::ofstream fout(filepath, std::ios::out); fout.is_open()) {
576       for (const auto& [node_name, time_series] : timing_map_) {
577         fout << node_name << " ";
578         for (const auto& slot : time_series) {
579           fout << (slot.end - slot.start) * kNanoToMilli << " ";
580         }
581         fout << "\n";
582       }
583       fout.close();
584     }
585   }
586 };
587 
588 // Dummpy profiler class.
589 class ProfilerDummy : public Profiler {
590  public:
ProfilerDummy()591   ProfilerDummy(){};
~ProfilerDummy()592   ~ProfilerDummy(){};
593 
SetUseCase(std::string)594   void SetUseCase(std::string) override final{};
SetDumpFilePrefix(const std::string &)595   void SetDumpFilePrefix(const std::string&) override final{};
Start(const std::string &,int)596   void Start(const std::string&, int) override final{};
End(const std::string &,int)597   void End(const std::string&, int) override final{};
PrintResult()598   void PrintResult() override final{};
ProfileFrameRate(const std::string &)599   void ProfileFrameRate(const std::string&) override final{};
SetFpsPrintInterval(int32_t)600   void SetFpsPrintInterval(int32_t) override final{};
GetLatencyData()601   std::list<std::pair<std::string, float>> GetLatencyData() override final {
602     return {};
603   }
GetUseCase() const604   std::string GetUseCase() const override final {
605     return "";
606   }
607 };
608 
609 }  // anonymous namespace
610 
Create(int option)611 std::shared_ptr<Profiler> Profiler::Create(int option) {
612   SetPropFlag flag = static_cast<SetPropFlag>(option);
613 
614   if (flag == SetPropFlag::kDisable) {
615     return std::make_shared<ProfilerDummy>();
616   } else if (flag & SetPropFlag::kStopWatch) {
617     return std::make_shared<ProfilerStopwatchImpl>(flag);
618   } else {
619     return std::make_shared<ProfilerImpl>(flag);
620   }
621 }
622 
623 }  // namespace camera_common
624 }  // namespace google
625