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