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