| /* |
| * Copyright (C) 2016 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| #ifndef HWBINDER_PERF_TEST_H |
| #define HWBINDER_PERF_TEST_H |
| |
| #include <unistd.h> |
| #include <chrono> |
| #include <list> |
| #include <tuple> |
| |
| #define TRACE_PATH "/sys/kernel/debug/tracing" |
| |
| using std::list; |
| using std::tuple; |
| |
| // Pipe is a object used for IPC between parent process and child process. |
| // This IPC class is widely used in binder/hwbinder tests. |
| // The common usage is the main process to create the Pipe and forks. |
| // Both parent and child hold a object. Each recv() on one side requires |
| // a send() on the other side to unblock. |
| class Pipe { |
| public: |
| static tuple<Pipe, Pipe> createPipePair(); |
| Pipe(Pipe&& rval) noexcept; |
| ~Pipe(); |
| inline void signal() { |
| bool val = true; |
| send(val); |
| } |
| inline void wait() { |
| bool val = false; |
| recv(val); |
| } |
| |
| // write a data struct |
| template <typename T> |
| int send(const T& v) { |
| return write(fd_write_, &v, sizeof(T)); |
| } |
| // read a data struct |
| template <typename T> |
| int recv(T& v) { |
| return read(fd_read_, &v, sizeof(T)); |
| } |
| |
| private: |
| int fd_read_; // file descriptor to read |
| int fd_write_; // file descriptor to write |
| Pipe(int read_fd, int write_fd) : fd_read_{read_fd}, fd_write_{write_fd} {} |
| Pipe(const Pipe&) = delete; |
| Pipe& operator=(const Pipe&) = delete; |
| Pipe& operator=(const Pipe&&) = delete; |
| }; |
| |
| // statistics of latency |
| // common usage: |
| // |
| // Results r; |
| // Tick sta, end; |
| // TICK_NOW(sta); |
| // ... do something ... |
| // TICK_NOW(end); |
| // r.addTime(tickDiffNS(sta, end)); |
| // |
| // r.dump(); |
| // r.dumpDistribution(); |
| // |
| class Results { |
| public: |
| // enable the deadline miss detection which stops the trace recording after |
| // a transaction latency > deadline_us_ is detected. |
| void setTracingMode(bool tracing, uint64_t deadline_us) { |
| tracing_ = tracing; |
| deadline_us_ = deadline_us; |
| } |
| inline uint64_t getTransactions() const { return transactions_; } |
| inline bool missDeadline(uint64_t nano) const { return nano > deadline_us_ * 1000; } |
| // Combine two sets of latency data points and update the aggregation info. |
| static Results combine(const Results& a, const Results& b); |
| // add a new transaction latency record |
| void addTime(uint64_t nano); |
| // prepare for raw data recording, it may allocate resources which requires |
| // a flushRawData() to release |
| void setupRawData(); |
| // dump the raw data and release the resource |
| void flushRawData(); |
| // dump average, best, worst latency in json |
| void dump() const; |
| // dump latency distribution in json |
| void dumpDistribution() const; |
| |
| private: |
| static const uint32_t kNumBuckets = 128; |
| static const uint64_t kMaxTimeBucket = 50ull * 1000000; |
| static const uint64_t kTimePerBucket = kMaxTimeBucket / kNumBuckets; |
| static constexpr float kTimePerBucketMS = kTimePerBucket / 1.0E6; |
| uint64_t best_ = 0xffffffffffffffffULL; // best transaction latency in ns. |
| uint64_t worst_ = 0; // worst transaction latency in ns. |
| uint64_t transactions_ = 0; // number of transactions |
| uint64_t total_time_ = 0; // total transaction time |
| uint64_t miss_ = 0; // number of transactions whose latency > deadline |
| uint32_t buckets_[kNumBuckets] = {0}; // statistics for the distribution |
| list<uint64_t>* raw_data_ = nullptr; // list for raw-data |
| bool tracing_ = false; // halt the trace log on a deadline miss |
| bool raw_dump_ = false; // record the raw data for the dump after |
| uint64_t deadline_us_ = 2500; // latency deadline in us. |
| }; |
| |
| // statistics of a process pair |
| class PResults { |
| public: |
| static PResults combine(const PResults& a, const PResults& b); |
| int nNotInherent = 0; ///< #transactions that does not inherit priority |
| int nNotSync = 0; ///< #transactions that are not synced |
| Results other; ///< statistics of CFS-other transactions |
| Results fifo; ///< statistics of RT-fifo transactions |
| // dump and flush the raw data |
| inline void flushRawData() { fifo.flushRawData(); } |
| // dump in json |
| void dump() const; |
| }; |
| |
| // Tick keeps timestamp |
| typedef std::chrono::time_point<std::chrono::high_resolution_clock> Tick; |
| |
| // get current timestamp as a Tick |
| static inline Tick tickNow() { |
| return std::chrono::high_resolution_clock::now(); |
| } |
| |
| #define TICK_NOW(_tick) \ |
| do { \ |
| asm volatile("" ::: "memory"); \ |
| _tick = tickNow(); \ |
| asm volatile("" ::: "memory"); \ |
| } while (0) |
| |
| // get nano seconds between sta & end |
| static inline uint64_t tickDiffNS(Tick& sta, Tick& end) { |
| return uint64_t(std::chrono::duration_cast<std::chrono::nanoseconds>(end - sta).count()); |
| } |
| #endif |