Home | History | Annotate | Download | only in simpleperf
      1 /*
      2  * Copyright (C) 2015 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 "record.h"
     18 
     19 #include <inttypes.h>
     20 #include <unordered_map>
     21 
     22 #include <base/logging.h>
     23 #include <base/stringprintf.h>
     24 
     25 #include "environment.h"
     26 #include "utils.h"
     27 
     28 static std::string RecordTypeToString(int record_type) {
     29   static std::unordered_map<int, std::string> record_type_names = {
     30       {PERF_RECORD_MMAP, "mmap"},
     31       {PERF_RECORD_LOST, "lost"},
     32       {PERF_RECORD_COMM, "comm"},
     33       {PERF_RECORD_EXIT, "exit"},
     34       {PERF_RECORD_THROTTLE, "throttle"},
     35       {PERF_RECORD_UNTHROTTLE, "unthrottle"},
     36       {PERF_RECORD_FORK, "fork"},
     37       {PERF_RECORD_READ, "read"},
     38       {PERF_RECORD_SAMPLE, "sample"},
     39       {PERF_RECORD_BUILD_ID, "build_id"},
     40   };
     41 
     42   auto it = record_type_names.find(record_type);
     43   if (it != record_type_names.end()) {
     44     return it->second;
     45   }
     46   return android::base::StringPrintf("unknown(%d)", record_type);
     47 }
     48 
     49 template <class T>
     50 void MoveFromBinaryFormat(T& data, const char*& p) {
     51   data = *reinterpret_cast<const T*>(p);
     52   p += sizeof(T);
     53 }
     54 
     55 template <class T>
     56 void MoveToBinaryFormat(const T& data, char*& p) {
     57   *reinterpret_cast<T*>(p) = data;
     58   p += sizeof(T);
     59 }
     60 
     61 SampleId::SampleId() {
     62   memset(this, 0, sizeof(SampleId));
     63 }
     64 
     65 // Return sample_id size in binary format.
     66 size_t SampleId::CreateContent(const perf_event_attr& attr) {
     67   sample_id_all = attr.sample_id_all;
     68   sample_type = attr.sample_type;
     69   // Other data are not necessary. TODO: Set missing SampleId data.
     70   size_t size = 0;
     71   if (sample_id_all) {
     72     if (sample_type & PERF_SAMPLE_TID) {
     73       size += sizeof(PerfSampleTidType);
     74     }
     75     if (sample_type & PERF_SAMPLE_TIME) {
     76       size += sizeof(PerfSampleTimeType);
     77     }
     78     if (sample_type & PERF_SAMPLE_ID) {
     79       size += sizeof(PerfSampleIdType);
     80     }
     81     if (sample_type & PERF_SAMPLE_STREAM_ID) {
     82       size += sizeof(PerfSampleStreamIdType);
     83     }
     84     if (sample_type & PERF_SAMPLE_CPU) {
     85       size += sizeof(PerfSampleCpuType);
     86     }
     87   }
     88   return size;
     89 }
     90 
     91 void SampleId::ReadFromBinaryFormat(const perf_event_attr& attr, const char* p, const char* end) {
     92   sample_id_all = attr.sample_id_all;
     93   sample_type = attr.sample_type;
     94   if (sample_id_all) {
     95     if (sample_type & PERF_SAMPLE_TID) {
     96       MoveFromBinaryFormat(tid_data, p);
     97     }
     98     if (sample_type & PERF_SAMPLE_TIME) {
     99       MoveFromBinaryFormat(time_data, p);
    100     }
    101     if (sample_type & PERF_SAMPLE_ID) {
    102       MoveFromBinaryFormat(id_data, p);
    103     }
    104     if (sample_type & PERF_SAMPLE_STREAM_ID) {
    105       MoveFromBinaryFormat(stream_id_data, p);
    106     }
    107     if (sample_type & PERF_SAMPLE_CPU) {
    108       MoveFromBinaryFormat(cpu_data, p);
    109     }
    110     // TODO: Add parsing of PERF_SAMPLE_IDENTIFIER.
    111   }
    112   CHECK_LE(p, end);
    113   if (p < end) {
    114     LOG(DEBUG) << "Record SampleId part has " << end - p << " bytes left\n";
    115   }
    116 }
    117 
    118 void SampleId::WriteToBinaryFormat(char*& p) const {
    119   if (sample_id_all) {
    120     if (sample_type & PERF_SAMPLE_TID) {
    121       MoveToBinaryFormat(tid_data, p);
    122     }
    123     if (sample_type & PERF_SAMPLE_TIME) {
    124       MoveToBinaryFormat(time_data, p);
    125     }
    126     if (sample_type & PERF_SAMPLE_ID) {
    127       MoveToBinaryFormat(id_data, p);
    128     }
    129     if (sample_type & PERF_SAMPLE_STREAM_ID) {
    130       MoveToBinaryFormat(stream_id_data, p);
    131     }
    132     if (sample_type & PERF_SAMPLE_CPU) {
    133       MoveToBinaryFormat(cpu_data, p);
    134     }
    135   }
    136 }
    137 
    138 void SampleId::Dump(size_t indent) const {
    139   if (sample_id_all) {
    140     if (sample_type & PERF_SAMPLE_TID) {
    141       PrintIndented(indent, "sample_id: pid %u, tid %u\n", tid_data.pid, tid_data.tid);
    142     }
    143     if (sample_type & PERF_SAMPLE_TIME) {
    144       PrintIndented(indent, "sample_id: time %" PRId64 "\n", time_data.time);
    145     }
    146     if (sample_type & PERF_SAMPLE_ID) {
    147       PrintIndented(indent, "sample_id: stream_id %" PRId64 "\n", id_data.id);
    148     }
    149     if (sample_type & PERF_SAMPLE_STREAM_ID) {
    150       PrintIndented(indent, "sample_id: stream_id %" PRId64 "\n", stream_id_data.stream_id);
    151     }
    152     if (sample_type & PERF_SAMPLE_CPU) {
    153       PrintIndented(indent, "sample_id: cpu %u, res %u\n", cpu_data.cpu, cpu_data.res);
    154     }
    155   }
    156 }
    157 
    158 Record::Record() {
    159   memset(&header, 0, sizeof(header));
    160 }
    161 
    162 Record::Record(const perf_event_header* pheader) {
    163   header = *pheader;
    164 }
    165 
    166 void Record::Dump(size_t indent) const {
    167   PrintIndented(indent, "record %s: type %u, misc %u, size %u\n",
    168                 RecordTypeToString(header.type).c_str(), header.type, header.misc, header.size);
    169   DumpData(indent + 1);
    170   sample_id.Dump(indent + 1);
    171 }
    172 
    173 MmapRecord::MmapRecord(const perf_event_attr& attr, const perf_event_header* pheader)
    174     : Record(pheader) {
    175   const char* p = reinterpret_cast<const char*>(pheader + 1);
    176   const char* end = reinterpret_cast<const char*>(pheader) + pheader->size;
    177   MoveFromBinaryFormat(data, p);
    178   filename = p;
    179   p += ALIGN(filename.size() + 1, 8);
    180   CHECK_LE(p, end);
    181   sample_id.ReadFromBinaryFormat(attr, p, end);
    182 }
    183 
    184 void MmapRecord::DumpData(size_t indent) const {
    185   PrintIndented(indent, "pid %u, tid %u, addr %p, len 0x%" PRIx64 "\n", data.pid, data.tid,
    186                 reinterpret_cast<void*>(data.addr), data.len);
    187   PrintIndented(indent, "pgoff 0x%" PRIx64 ", filename %s\n", data.pgoff, filename.c_str());
    188 }
    189 
    190 std::vector<char> MmapRecord::BinaryFormat() const {
    191   std::vector<char> buf(header.size);
    192   char* p = buf.data();
    193   MoveToBinaryFormat(header, p);
    194   MoveToBinaryFormat(data, p);
    195   strcpy(p, filename.c_str());
    196   p += ALIGN(filename.size() + 1, 8);
    197   sample_id.WriteToBinaryFormat(p);
    198   return buf;
    199 }
    200 
    201 CommRecord::CommRecord(const perf_event_attr& attr, const perf_event_header* pheader)
    202     : Record(pheader) {
    203   const char* p = reinterpret_cast<const char*>(pheader + 1);
    204   const char* end = reinterpret_cast<const char*>(pheader) + pheader->size;
    205   MoveFromBinaryFormat(data, p);
    206   comm = p;
    207   p += ALIGN(strlen(p) + 1, 8);
    208   CHECK_LE(p, end);
    209   sample_id.ReadFromBinaryFormat(attr, p, end);
    210 }
    211 
    212 void CommRecord::DumpData(size_t indent) const {
    213   PrintIndented(indent, "pid %u, tid %u, comm %s\n", data.pid, data.tid, comm.c_str());
    214 }
    215 
    216 std::vector<char> CommRecord::BinaryFormat() const {
    217   std::vector<char> buf(header.size);
    218   char* p = buf.data();
    219   MoveToBinaryFormat(header, p);
    220   MoveToBinaryFormat(data, p);
    221   strcpy(p, comm.c_str());
    222   p += ALIGN(comm.size() + 1, 8);
    223   sample_id.WriteToBinaryFormat(p);
    224   return buf;
    225 }
    226 
    227 ExitRecord::ExitRecord(const perf_event_attr& attr, const perf_event_header* pheader)
    228     : Record(pheader) {
    229   const char* p = reinterpret_cast<const char*>(pheader + 1);
    230   const char* end = reinterpret_cast<const char*>(pheader) + pheader->size;
    231   MoveFromBinaryFormat(data, p);
    232   CHECK_LE(p, end);
    233   sample_id.ReadFromBinaryFormat(attr, p, end);
    234 }
    235 
    236 void ExitRecord::DumpData(size_t indent) const {
    237   PrintIndented(indent, "pid %u, ppid %u, tid %u, ptid %u\n", data.pid, data.ppid, data.tid,
    238                 data.ptid);
    239 }
    240 
    241 SampleRecord::SampleRecord(const perf_event_attr& attr, const perf_event_header* pheader)
    242     : Record(pheader) {
    243   const char* p = reinterpret_cast<const char*>(pheader + 1);
    244   const char* end = reinterpret_cast<const char*>(pheader) + pheader->size;
    245   sample_type = attr.sample_type;
    246 
    247   if (sample_type & PERF_SAMPLE_IP) {
    248     MoveFromBinaryFormat(ip_data, p);
    249   }
    250   if (sample_type & PERF_SAMPLE_TID) {
    251     MoveFromBinaryFormat(tid_data, p);
    252   }
    253   if (sample_type & PERF_SAMPLE_TIME) {
    254     MoveFromBinaryFormat(time_data, p);
    255   }
    256   if (sample_type & PERF_SAMPLE_ADDR) {
    257     MoveFromBinaryFormat(addr_data, p);
    258   }
    259   if (sample_type & PERF_SAMPLE_ID) {
    260     MoveFromBinaryFormat(id_data, p);
    261   }
    262   if (sample_type & PERF_SAMPLE_STREAM_ID) {
    263     MoveFromBinaryFormat(stream_id_data, p);
    264   }
    265   if (sample_type & PERF_SAMPLE_CPU) {
    266     MoveFromBinaryFormat(cpu_data, p);
    267   }
    268   if (sample_type & PERF_SAMPLE_PERIOD) {
    269     MoveFromBinaryFormat(period_data, p);
    270   }
    271   // TODO: Add parsing of other PERF_SAMPLE_*.
    272   CHECK_LE(p, end);
    273   if (p < end) {
    274     LOG(DEBUG) << "Record has " << end - p << " bytes left\n";
    275   }
    276 }
    277 
    278 void SampleRecord::DumpData(size_t indent) const {
    279   PrintIndented(indent, "sample_type: 0x%" PRIx64 "\n", sample_type);
    280   if (sample_type & PERF_SAMPLE_IP) {
    281     PrintIndented(indent, "ip %p\n", reinterpret_cast<void*>(ip_data.ip));
    282   }
    283   if (sample_type & PERF_SAMPLE_TID) {
    284     PrintIndented(indent, "pid %u, tid %u\n", tid_data.pid, tid_data.tid);
    285   }
    286   if (sample_type & PERF_SAMPLE_TIME) {
    287     PrintIndented(indent, "time %" PRId64 "\n", time_data.time);
    288   }
    289   if (sample_type & PERF_SAMPLE_ADDR) {
    290     PrintIndented(indent, "addr %p\n", reinterpret_cast<void*>(addr_data.addr));
    291   }
    292   if (sample_type & PERF_SAMPLE_ID) {
    293     PrintIndented(indent, "id %" PRId64 "\n", id_data.id);
    294   }
    295   if (sample_type & PERF_SAMPLE_STREAM_ID) {
    296     PrintIndented(indent, "stream_id %" PRId64 "\n", stream_id_data.stream_id);
    297   }
    298   if (sample_type & PERF_SAMPLE_CPU) {
    299     PrintIndented(indent, "cpu %u, res %u\n", cpu_data.cpu, cpu_data.res);
    300   }
    301   if (sample_type & PERF_SAMPLE_PERIOD) {
    302     PrintIndented(indent, "period %" PRId64 "\n", period_data.period);
    303   }
    304 }
    305 
    306 BuildIdRecord::BuildIdRecord(const perf_event_header* pheader) : Record(pheader) {
    307   const char* p = reinterpret_cast<const char*>(pheader + 1);
    308   const char* end = reinterpret_cast<const char*>(pheader) + pheader->size;
    309   MoveFromBinaryFormat(pid, p);
    310   std::copy_n(p, build_id.size(), build_id.begin());
    311   p += ALIGN(build_id.size(), 8);
    312   filename = p;
    313   p += ALIGN(filename.size() + 1, 64);
    314   CHECK_EQ(p, end);
    315 }
    316 
    317 void BuildIdRecord::DumpData(size_t indent) const {
    318   PrintIndented(indent, "pid %u\n", pid);
    319   PrintIndented(indent, "build_id 0x");
    320   for (auto& c : build_id) {
    321     printf("%02x", c);
    322   }
    323   printf("\n");
    324   PrintIndented(indent, "filename %s\n", filename.c_str());
    325 }
    326 
    327 std::vector<char> BuildIdRecord::BinaryFormat() const {
    328   std::vector<char> buf(header.size);
    329   char* p = buf.data();
    330   MoveToBinaryFormat(header, p);
    331   MoveToBinaryFormat(pid, p);
    332   memcpy(p, build_id.data(), build_id.size());
    333   p += ALIGN(build_id.size(), 8);
    334   strcpy(p, filename.c_str());
    335   p += ALIGN(filename.size() + 1, 64);
    336   return buf;
    337 }
    338 
    339 std::unique_ptr<const Record> ReadRecordFromBuffer(const perf_event_attr& attr,
    340                                                    const perf_event_header* pheader) {
    341   switch (pheader->type) {
    342     case PERF_RECORD_MMAP:
    343       return std::unique_ptr<const Record>(new MmapRecord(attr, pheader));
    344     case PERF_RECORD_COMM:
    345       return std::unique_ptr<const Record>(new CommRecord(attr, pheader));
    346     case PERF_RECORD_EXIT:
    347       return std::unique_ptr<const Record>(new ExitRecord(attr, pheader));
    348     case PERF_RECORD_SAMPLE:
    349       return std::unique_ptr<const Record>(new SampleRecord(attr, pheader));
    350     default:
    351       return std::unique_ptr<const Record>(new Record(pheader));
    352   }
    353 }
    354 
    355 MmapRecord CreateMmapRecord(const perf_event_attr& attr, bool in_kernel, uint32_t pid, uint32_t tid,
    356                             uint64_t addr, uint64_t len, uint64_t pgoff,
    357                             const std::string& filename) {
    358   MmapRecord record;
    359   record.header.type = PERF_RECORD_MMAP;
    360   record.header.misc = (in_kernel ? PERF_RECORD_MISC_KERNEL : PERF_RECORD_MISC_USER);
    361   record.data.pid = pid;
    362   record.data.tid = tid;
    363   record.data.addr = addr;
    364   record.data.len = len;
    365   record.data.pgoff = pgoff;
    366   record.filename = filename;
    367   size_t sample_id_size = record.sample_id.CreateContent(attr);
    368   record.header.size = sizeof(record.header) + sizeof(record.data) +
    369                        ALIGN(record.filename.size() + 1, 8) + sample_id_size;
    370   return record;
    371 }
    372 
    373 CommRecord CreateCommRecord(const perf_event_attr& attr, uint32_t pid, uint32_t tid,
    374                             const std::string& comm) {
    375   CommRecord record;
    376   record.header.type = PERF_RECORD_COMM;
    377   record.header.misc = 0;
    378   record.data.pid = pid;
    379   record.data.tid = tid;
    380   record.comm = comm;
    381   size_t sample_id_size = record.sample_id.CreateContent(attr);
    382   record.header.size = sizeof(record.header) + sizeof(record.data) +
    383                        ALIGN(record.comm.size() + 1, 8) + sample_id_size;
    384   return record;
    385 }
    386 
    387 BuildIdRecord CreateBuildIdRecord(bool in_kernel, pid_t pid, const BuildId& build_id,
    388                                   const std::string& filename) {
    389   BuildIdRecord record;
    390   record.header.type = PERF_RECORD_BUILD_ID;
    391   record.header.misc = (in_kernel ? PERF_RECORD_MISC_KERNEL : PERF_RECORD_MISC_USER);
    392   record.pid = pid;
    393   record.build_id = build_id;
    394   record.filename = filename;
    395   record.header.size = sizeof(record.header) + sizeof(record.pid) +
    396                        ALIGN(record.build_id.size(), 8) + ALIGN(filename.size() + 1, 64);
    397   return record;
    398 }
    399