1 /* 2 * Copyright (C) 2016 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 #include "perf_clock.h" 18 19 #include <sys/mman.h> 20 #include <sys/syscall.h> 21 22 #include <atomic> 23 #include <chrono> 24 #include <thread> 25 26 #include <android-base/logging.h> 27 28 #include "environment.h" 29 #include "event_attr.h" 30 #include "event_fd.h" 31 #include "event_type.h" 32 #include "record.h" 33 34 static bool perf_clock_initialized = false; 35 static int64_t perf_clock_and_system_clock_diff_in_ns = 0; 36 37 struct ThreadArg { 38 std::atomic<pid_t> thread_a_tid; 39 std::atomic<bool> start_mmap; 40 std::atomic<uint64_t> mmap_start_addr; 41 uint64_t system_time_in_ns; 42 std::atomic<bool> has_error; 43 }; 44 45 static void ThreadA(ThreadArg* thread_arg) { 46 thread_arg->thread_a_tid = syscall(SYS_gettid); 47 while (!thread_arg->start_mmap) { 48 usleep(1000); 49 } 50 51 size_t TRY_MMAP_COUNT = 10; 52 53 struct TryMmap { 54 void* mmap_start_addr; 55 uint64_t start_system_time_in_ns; 56 uint64_t end_system_time_in_ns; 57 }; 58 TryMmap array[TRY_MMAP_COUNT]; 59 60 // In case current thread is preempted by other threads, we run mmap() 61 // multiple times and use the one with the smallest time interval. 62 for (size_t i = 0; i < TRY_MMAP_COUNT; ++i) { 63 array[i].start_system_time_in_ns = GetSystemClock(); 64 array[i].mmap_start_addr = 65 mmap(NULL, 4096, PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 66 if (array[i].mmap_start_addr == MAP_FAILED) { 67 PLOG(ERROR) << "mmap() failed"; 68 thread_arg->has_error = true; 69 return; 70 } 71 72 array[i].end_system_time_in_ns = GetSystemClock(); 73 } 74 size_t best_index = 0; 75 uint64_t min_duration_in_ns = UINT64_MAX; 76 for (size_t i = 0; i < TRY_MMAP_COUNT; ++i) { 77 uint64_t d = 78 array[i].end_system_time_in_ns - array[i].start_system_time_in_ns; 79 if (min_duration_in_ns > d) { 80 min_duration_in_ns = d; 81 best_index = i; 82 } 83 munmap(array[i].mmap_start_addr, 4096); 84 } 85 thread_arg->mmap_start_addr = 86 reinterpret_cast<uint64_t>(array[best_index].mmap_start_addr); 87 // Perf time is generated at the end of mmap() syscall, which is close to 88 // the end time instead of the start time. 89 thread_arg->system_time_in_ns = array[best_index].end_system_time_in_ns; 90 } 91 92 static bool GetClockDiff(int64_t* clock_diff_in_ns) { 93 ThreadArg thread_arg; 94 thread_arg.thread_a_tid = 0; 95 thread_arg.start_mmap = false; 96 thread_arg.has_error = false; 97 std::thread thread_a(ThreadA, &thread_arg); 98 while (thread_arg.thread_a_tid == 0) { 99 usleep(1000); 100 } 101 std::unique_ptr<EventTypeAndModifier> event_type = 102 ParseEventType("cpu-clock"); 103 if (event_type == nullptr) { 104 return false; 105 } 106 perf_event_attr attr = CreateDefaultPerfEventAttr(event_type->event_type); 107 attr.comm = 0; 108 attr.mmap_data = 1; 109 attr.mmap = 0; 110 attr.inherit = 0; 111 attr.sample_id_all = 1; 112 attr.freq = 0; 113 attr.sample_period = 1ULL << 62; // Sample records are not needed. 114 std::unique_ptr<EventFd> event_fd = 115 EventFd::OpenEventFile(attr, thread_arg.thread_a_tid, -1, nullptr); 116 if (event_fd == nullptr) { 117 return false; 118 } 119 if (!event_fd->CreateMappedBuffer(4, true)) { 120 return false; 121 } 122 123 thread_arg.start_mmap = true; 124 thread_a.join(); 125 126 if (thread_arg.has_error) { 127 return false; 128 } 129 130 std::vector<char> buffer; 131 size_t buffer_pos = 0; 132 size_t size = event_fd->GetAvailableMmapData(buffer, buffer_pos); 133 std::vector<std::unique_ptr<Record>> records = 134 ReadRecordsFromBuffer(attr, buffer.data(), size); 135 uint64_t perf_time_in_ns = 0; 136 for (auto& r : records) { 137 if (r->type() == PERF_RECORD_MMAP) { 138 auto& record = *static_cast<MmapRecord*>(r.get()); 139 if (record.data->addr == thread_arg.mmap_start_addr) { 140 perf_time_in_ns = record.Timestamp(); 141 } 142 } 143 } 144 if (perf_time_in_ns == 0) { 145 LOG(ERROR) << "GetPerfClockAndSystemClockDiff: can't get perf time."; 146 return false; 147 } 148 149 *clock_diff_in_ns = perf_time_in_ns - thread_arg.system_time_in_ns; 150 LOG(VERBOSE) << "perf_time is " << perf_time_in_ns << " ns, system_time is " 151 << thread_arg.system_time_in_ns << " ns , clock_diff is " 152 << *clock_diff_in_ns << " ns."; 153 return true; 154 } 155 156 bool InitPerfClock() { 157 if (!perf_clock_initialized) { 158 if (!GetClockDiff(&perf_clock_and_system_clock_diff_in_ns)) { 159 return false; 160 } 161 perf_clock_initialized = true; 162 } 163 return true; 164 } 165 166 uint64_t GetPerfClock() { 167 CHECK(perf_clock_initialized); 168 return GetSystemClock() + perf_clock_and_system_clock_diff_in_ns; 169 } 170