[LNT] r263560 - [profile] Add perf and cPerf, for importing Linux Perf profiles

Renato Golin via llvm-commits llvm-commits at lists.llvm.org
Tue Mar 15 13:56:22 PDT 2016


Hi James,

I think this patch has broken our bot...

http://lab.llvm.org:8011/builders/clang-native-arm-lnt/builds/16907/steps/setup%20lit/logs/stdio

--renato

On 15 March 2016 at 16:52, James Molloy via llvm-commits
<llvm-commits at lists.llvm.org> wrote:
> Author: jamesm
> Date: Tue Mar 15 11:52:20 2016
> New Revision: 263560
>
> URL: http://llvm.org/viewvc/llvm-project?rev=263560&view=rev
> Log:
> [profile] Add perf and cPerf, for importing Linux Perf profiles
>
> cPerf is a C++ python extension that reads perf.data sample streams and aggregates the data (including running objdump) and producing a ProfileV1 object.
>
> The module is marked "optional" in setup.py, which means that if it fails to build it shouldn't fail setup (and perf.py allows module import to fail). However the version of distutils I have here on a mac doesn't understand that option, so hopefully it won't fail to build...
>
> Initially I implemented this as a simple regex wrapper around 'perf annotate' that grepped the counter values and objdump output from perf's textual output.
>
> Writing this in C gained me about a 100x speedup. In fact, this C implementation is around 6x as fast as perf itself, probably because perf does more magic.
>
> An optimization here is that we ignore any binary with < 1% of the samples. This in practice means we don't waste time nm'ing and objdumping things like "perf_4.0" and "/bin/bash" or most of the dynamic libraries. This also gives a rather large speedup.
>
> Added:
>     lnt/trunk/lnt/testing/profile/cPerf.cpp
>     lnt/trunk/lnt/testing/profile/perf.py
>     lnt/trunk/tests/testing/Inputs/fake-nm.py
>     lnt/trunk/tests/testing/Inputs/fake-objdump.py
>     lnt/trunk/tests/testing/Inputs/fib-aarch64.nm.out
>     lnt/trunk/tests/testing/Inputs/fib-aarch64.objdump.0x4006c8.out
>     lnt/trunk/tests/testing/Inputs/fib-aarch64.perf_data
>     lnt/trunk/tests/testing/Inputs/fib2-aarch64.nm.out
>     lnt/trunk/tests/testing/Inputs/fib2-aarch64.objdump.0x4006c8.out
>     lnt/trunk/tests/testing/Inputs/fib2-aarch64.perf_data
>     lnt/trunk/tests/testing/cPerf.py
> Modified:
>     lnt/trunk/setup.py
>     lnt/trunk/tests/lit.cfg
>
> Added: lnt/trunk/lnt/testing/profile/cPerf.cpp
> URL: http://llvm.org/viewvc/llvm-project/lnt/trunk/lnt/testing/profile/cPerf.cpp?rev=263560&view=auto
> ==============================================================================
> --- lnt/trunk/lnt/testing/profile/cPerf.cpp (added)
> +++ lnt/trunk/lnt/testing/profile/cPerf.cpp Tue Mar 15 11:52:20 2016
> @@ -0,0 +1,754 @@
> +//===-cPerf.cpp - Linux perf.data parsing ---------------------------------===//
> +//
> +//                     The LLVM Compiler Infrastructure
> +//
> +// This file is distributed under the University of Illinois Open Source
> +// License. See LICENSE.TXT for details.
> +//
> +//===----------------------------------------------------------------------===//
> +//
> +// This is a python module that reads perf.data files generated by Linux Perf.
> +// Writing efficiently-executing Python that reads and manipulates binary files
> +// is difficult and results in code that looks very un-Python-like.
> +//
> +// Also, parsing profiles is on the critical path for result submission and
> +// often has to run on target devices that may not be as fast as a desktop
> +// machine (an example is a pandaboard - people still test on Cortex-A9).
> +//
> +// This algorithm aims to be faster than 'perf report' or 'perf annotate'.
> +// In experiments this module is around 6x faster than 'perf annotate' - more
> +// when we reduce the amount of data imported (see later).
> +//
> +// Algorithm
> +// ---------
> +//
> +// We start by mmapping the perf.data file and reading the header, event
> +// attribute entries and the sample data stream. Every sample is aggregated into
> +// a set of counters counting every event for every PC location (indexed by mmap
> +// ID - a data stream can contain samples for the same PC in different binaries
> +// e.g. if exec() is called). Total counters are also kept for the entire stream
> +// and per-mmap.
> +//
> +// The source for the perf.data format is here: https://lwn.net/Articles/644919/
> +// and the perf_events manpage.
> +//
> +// Once the aggregation is done, we do a single pass through the event data:
> +//
> +//   for each mmap:
> +//       if the mmap's event total is < 1% of the total in all counters,
> +//         then discard the mmap.
> +//
> +//       load symbol data by calling "nm" and parsing the result.
> +//       for all PCs we have events for, in sorted order:
> +//           find the symbol this PC is part of -> Sym
> +//           look up all PCs between Sym.Start and Sym.End and emit data
> +//
> +// The output of this module (cPerf.importPerf) is a dictionary in (almost)
> +// ProfileV1 form. The only difference is that all counters are absolute.
> +//
> +//===----------------------------------------------------------------------===//
> +
> +#ifndef STANDALONE
> +#include <Python.h>
> +#endif
> +#include <algorithm>
> +#include <cassert>
> +#include <cstring>
> +#include <exception>
> +#include <fcntl.h>
> +#include <iostream>
> +#include <map>
> +#include <sstream>
> +#include <stdint.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <sys/mman.h>
> +#include <sys/stat.h>
> +#include <sys/wait.h>
> +#include <unistd.h>
> +#include <vector>
> +
> +//===----------------------------------------------------------------------===//
> +// Helpers
> +//===----------------------------------------------------------------------===//
> +
> +#ifndef STANDALONE
> +#ifdef assert
> +#undef assert
> +#endif
> +
> +// Redefine assert so we can use it without destroying the Python interpreter!
> +#define assert(expr) Assert((expr), #expr, __FILE__, __LINE__)
> +
> +#endif
> +
> +// From http://stackoverflow.com/questions/2417588/escaping-a-c-string
> +template<class OutIter>
> +OutIter write_escaped(std::string const& s, OutIter out) {
> +  *out++ = '"';
> +  for (std::string::const_iterator i = s.begin(), end = s.end(); i != end; ++i) {
> +    unsigned char c = *i;
> +    if (' ' <= c and c <= '~' and c != '\\' and c != '"') {
> +      *out++ = c;
> +    }
> +    else {
> +      *out++ = '\\';
> +      switch(c) {
> +      case '"':  *out++ = '"';  break;
> +      case '\\': *out++ = '\\'; break;
> +      case '\t': *out++ = 't';  break;
> +      case '\r': *out++ = 'r';  break;
> +      case '\n': *out++ = 'n';  break;
> +      default:
> +        char const* const hexdig = "0123456789ABCDEF";
> +        *out++ = 'x';
> +        *out++ = hexdig[c >> 4];
> +        *out++ = hexdig[c & 0xF];
> +      }
> +    }
> +  }
> +  *out++ = '"';
> +  return out;
> +}
> +
> +// Remove a uint32_t from a byte stream
> +uint32_t TakeU32(unsigned char *&Buf) {
> +  uint32_t X = * (uint32_t*) Buf;
> +  Buf += sizeof(uint32_t);
> +  return X;
> +}
> +
> +// Remove a uint64_t from a byte stream
> +uint64_t TakeU64(unsigned char *&Buf) {
> +  uint64_t X = * (uint64_t*) Buf;
> +  Buf += sizeof(uint64_t);
> +  return X;
> +}
> +
> +// Forks, execs Cmd under a shell and returns
> +// a file descriptor reading the command's stdout.
> +FILE *ForkAndExec(std::string Cmd) {
> +  int P[2];
> +  pipe(P);
> +
> +  FILE *Stream = fdopen(P[0], "r");
> +
> +  if (fork() == 0) {
> +    dup2(P[1], 1);
> +    close(P[0]);
> +    execl("/bin/sh", "sh", "-c", Cmd.c_str(), 0);
> +  } else {
> +    close(P[1]);
> +  }
> +
> +  return Stream;
> +}
> +
> +void Assert(bool Expr, const char *ExprStr, const char *File, int Line) {
> +  if (Expr)
> +    return;
> +  char Str[512];
> +  sprintf(Str, "%s (%s:%d)", ExprStr, File, Line);
> +  throw std::logic_error(Str);
> +}
> +
> +//===----------------------------------------------------------------------===//
> +// Perf structures. Taken from https://lwn.net/Articles/644919/
> +//===----------------------------------------------------------------------===//
> +
> +#define PERF_RECORD_MMAP 1
> +#define PERF_RECORD_SAMPLE 9
> +#define PERF_RECORD_MMAP2 10
> +
> +#define PERF_SAMPLE_IP    (1U << 0)
> +#define PERF_SAMPLE_TID   (1U << 1)
> +#define PERF_SAMPLE_TIME  (1U << 2)
> +#define PERF_SAMPLE_ADDR  (1U << 3)
> +#define PERF_SAMPLE_ID    (1U << 6)
> +#define PERF_SAMPLE_CPU   (1U << 7)
> +#define PERF_SAMPLE_PERIOD (1U << 8)
> +#define PERF_SAMPLE_STREAM_ID (1U << 9)
> +#define PERF_SAMPLE_IDENTIFIER (1U << 16)
> +
> +struct perf_file_section {
> +  uint64_t offset; /* offset from start of file */
> +  uint64_t size;   /* size of the section */
> +};
> +
> +struct perf_header {
> +  char magic[8];      /* PERFILE2 */
> +  uint64_t size;      /* size of the header */
> +  uint64_t attr_size; /* size of an attribute in attrs */
> +  struct perf_file_section attrs;
> +  struct perf_file_section data;
> +  struct perf_file_section event_types;
> +  uint64_t flags;
> +  uint64_t flags1[3];
> +};
> +
> +struct perf_event_header {
> +  uint32_t type;
> +  uint16_t misc;
> +  uint16_t size;
> +};
> +
> +struct perf_event_sample {
> +  struct perf_event_header header;
> +
> +  uint64_t sample_id;
> +  uint64_t ip;
> +  uint32_t pid, tid;
> +  uint64_t time;
> +  uint64_t id;
> +  uint64_t period;
> +};
> +
> +struct perf_event_mmap {
> +  struct perf_event_header header;
> +
> +  uint32_t pid, tid;
> +  uint64_t start, extent, pgoff;
> +  char filename[1];
> +};
> +
> +struct perf_event_mmap2 {
> +  struct perf_event_header header;
> +
> +  uint32_t pid, tid;
> +  uint64_t start, extent, pgoff;
> +  uint32_t maj, min;
> +  uint64_t ino, ino_generation;
> +  uint32_t prot, flags;
> +  char filename[1];
> +};
> +
> +struct perf_trace_event_type {
> +  uint64_t event_id;
> +  char str[64];
> +};
> +
> +//===----------------------------------------------------------------------===//
> +// Readers for nm and objdump output
> +//===----------------------------------------------------------------------===//
> +
> +struct Map {
> +  uint64_t Start, End;
> +  const char *Filename;
> +};
> +
> +struct Symbol {
> +  uint64_t Start;
> +  uint64_t End;
> +  std::string Name;
> +
> +  bool operator<(const Symbol &Other) const { return Start < Other.Start; }
> +};
> +
> +class NmOutput : public std::vector<Symbol> {
> +public:
> +  std::string Nm;
> +
> +  NmOutput(std::string Nm) : Nm(Nm) {}
> +
> +  void reset(Map *M) {
> +    clear();
> +
> +    std::string Cmd = Nm + " -D -S --defined-only " + std::string(M->Filename) +
> +                      " 2>/dev/null";
> +    auto Stream = ForkAndExec(Cmd);
> +
> +    char *Line = nullptr;
> +    size_t LineLen = 0;
> +    while (true) {
> +      ssize_t Len = getline(&Line, &LineLen, Stream);
> +      if (Len == -1)
> +        break;
> +
> +      char One[32], Two[32], Three[32], Four[512];
> +      if (sscanf(Line, "%s %s %s %s", One, Two, Three, Four) < 4)
> +        continue;
> +
> +      char *EndPtr = NULL;
> +      uint64_t Start = strtoull(One, &EndPtr, 16);
> +      if (EndPtr == One)
> +        continue;
> +      uint64_t Extent = strtoull(Two, &EndPtr, 16);
> +      if (EndPtr == Two)
> +        continue;
> +      if (strlen(Three) != 1)
> +        continue;
> +      switch (Three[0]) {
> +      default:
> +        continue;
> +      case 'T':
> +      case 't': // Text section
> +      case 'V':
> +      case 'v': // Weak object
> +      case 'W':
> +      case 'w': // Weak object (not tagged as such)
> +        break;
> +      }
> +      std::string S(Four);
> +      if (S.back() == '\n')
> +        S.pop_back();
> +      push_back({Start, Start + Extent, S});
> +    }
> +    if (Line)
> +      free(Line);
> +
> +    fclose(Stream);
> +    wait(NULL);
> +    std::sort(begin(), end());
> +  }
> +};
> +
> +class ObjdumpOutput {
> +public:
> +  std::string Objdump;
> +  FILE *Stream;
> +  char *ThisText;
> +  uint64_t ThisAddress;
> +  uint64_t EndAddress;
> +  char *Line;
> +  size_t LineLen;
> +
> +  ObjdumpOutput(std::string Objdump)
> +    : Objdump(Objdump), Stream(nullptr), Line(NULL), LineLen(0) {}
> +  ~ObjdumpOutput() {
> +    if (Stream) {
> +      fclose(Stream);
> +      wait(NULL);
> +    }
> +    if (Line)
> +      free(Line);
> +  }
> +
> +  void reset(Map *M, uint64_t Start, uint64_t Stop) {
> +    ThisAddress = 0;
> +    if (Stream) {
> +      fclose(Stream);
> +      wait(NULL);
> +    }
> +
> +    char buf1[32], buf2[32];
> +    sprintf(buf1, "%#llx", Start);
> +    sprintf(buf2, "%#llx", Stop + 4);
> +
> +    std::string Cmd = Objdump + " -d --no-show-raw-insn --start-address=" +
> +                      std::string(buf1) + " --stop-address=" +
> +                      std::string(buf2) + " " + std::string(M->Filename) +
> +                      " 2>/dev/null";
> +    Stream = ForkAndExec(Cmd);
> +
> +    EndAddress = Stop;
> +  };
> +
> +  std::string getText() { return ThisText; }
> +
> +  uint64_t next() {
> +    getLine();
> +    return ThisAddress;
> +  }
> +
> +  void getLine() {
> +    while (true) {
> +      ssize_t Len = getline(&Line, &LineLen, Stream);
> +      if (Len == -1) {
> +        ThisAddress = EndAddress;
> +        return;
> +      }
> +      char *TokBuf;
> +      char *One = strtok_r(Line, ":", &TokBuf);
> +      char *Two = strtok_r(NULL, "\n", &TokBuf);
> +      if (!One || !Two)
> +        continue;
> +      char *EndPtr = NULL;
> +      uint64_t Address = strtoull(One, &EndPtr, 16);
> +      if (EndPtr != &One[strlen(One)])
> +        continue;
> +
> +      ThisAddress = Address;
> +      ThisText = Two;
> +      break;
> +    }
> +  }
> +};
> +
> +//===----------------------------------------------------------------------===//
> +// PerfReader
> +//===----------------------------------------------------------------------===//
> +
> +class PerfReader {
> +public:
> +  PerfReader(const std::string &Filename, std::string Nm,
> +             std::string Objdump);
> +  ~PerfReader();
> +
> +  void readHeader();
> +  void readAttrs();
> +  void readDataStream();
> +  unsigned char *readEvent(unsigned char *);
> +  perf_event_sample parseEvent(unsigned char *Buf, uint64_t Layout);
> +  void emitLine(uint64_t PC, std::map<const char *, uint64_t> *Counters,
> +                const std::string &Text, bool First);
> +  void emitFunctionStart(std::string &Name, bool First);
> +  void emitFunctionEnd(std::string &Name,
> +                       std::map<const char *, uint64_t> &Counters);
> +  void emitTopLevelCounters();
> +  void emitMaps();
> +  void emitSymbol(
> +      Symbol &Sym, Map &M,
> +      std::map<uint64_t, std::map<const char *, uint64_t>>::iterator &Event,
> +      bool FirstSym);
> +  PyObject *complete();
> +
> +private:
> +  unsigned char *Buffer;
> +  size_t BufferLen;
> +
> +  perf_header *Header;
> +  std::map<uint64_t, const char *> EventIDs;
> +  std::map<uint64_t, uint64_t> EventLayouts;
> +  std::map<size_t, std::map<uint64_t, std::map<const char *, uint64_t>>> Events;
> +  std::map<const char *, uint64_t> TotalEvents;
> +  std::map<uint64_t, std::map<const char *, uint64_t>> TotalEventsPerMap;
> +  std::vector<Map> Maps;
> +  std::map<uint64_t, size_t> CurrentMaps;
> +
> +  PyObject *Functions, *TopLevelCounters;
> +  std::vector<PyObject*> Lines;
> +
> +  std::string Nm, Objdump;
> +};
> +
> +PerfReader::PerfReader(const std::string &Filename,
> +                       std::string Nm, std::string Objdump)
> +    : Nm(Nm), Objdump(Objdump) {
> +  int fd = open(Filename.c_str(), O_RDONLY);
> +  assert(fd > 0);
> +
> +  struct stat sb;
> +  assert(fstat(fd, &sb) == 0);
> +  BufferLen = (size_t)sb.st_size;
> +
> +  Buffer = (unsigned char *)mmap(NULL, BufferLen, PROT_READ, MAP_SHARED, fd, 0);
> +  assert(Buffer != MAP_FAILED);
> +}
> +
> +PerfReader::~PerfReader() { munmap(Buffer, BufferLen); }
> +
> +void PerfReader::readHeader() {
> +  Header = (perf_header *)&Buffer[0];
> +
> +  assert(!strncmp(Header->magic, "PERFILE2", 8));
> +}
> +
> +void PerfReader::readDataStream() {
> +  unsigned char *Buf = &Buffer[Header->data.offset];
> +  unsigned char *End = Buf + Header->data.size;
> +  while (Buf < End)
> +    Buf = readEvent(Buf);
> +}
> +
> +void PerfReader::readAttrs() {
> +  const int HEADER_EVENT_DESC = 12;
> +  perf_file_section *P =
> +      (perf_file_section *)&Buffer[Header->data.offset + Header->data.size];
> +  for (int I = 0; I < HEADER_EVENT_DESC; ++I)
> +    if (Header->flags & (1U << I))
> +      ++P;
> +
> +  assert(Header->flags & (1U << HEADER_EVENT_DESC));
> +
> +  unsigned char *Buf = &Buffer[P->offset];
> +  uint32_t NumEvents = TakeU32(Buf);
> +  uint32_t AttrSize = TakeU32(Buf);
> +  for (unsigned I = 0; I < NumEvents; ++I) {
> +    // Parse out the "config" bitmask for the struct layout.
> +    auto *Buf2 = Buf;
> +    (void)TakeU32(Buf2); (void)TakeU32(Buf2); (void)TakeU64(Buf2); (void)TakeU64(Buf2);
> +    auto Layout = TakeU64(Buf2);
> +
> +    Buf += AttrSize;
> +    uint32_t NumIDs = TakeU32(Buf);
> +
> +    uint32_t StrLen = TakeU32(Buf);
> +    const char *Str = (const char *)Buf;
> +    Buf += StrLen;
> +
> +    // Weirdness of perf: if there is only one event descriptor, that
> +    // event descriptor can be referred to by ANY id!
> +    if (NumEvents == 1 && NumIDs == 0) {
> +      EventIDs[0] = Str;
> +      EventLayouts[0] = Layout;
> +    }
> +
> +    for (unsigned J = 0; J < NumIDs; ++J) {
> +      auto L = TakeU64(Buf);
> +      EventIDs[L] = Str;
> +      EventLayouts[L] = Layout;
> +    }
> +  }
> +}
> +
> +unsigned char *PerfReader::readEvent(unsigned char *Buf) {
> +  perf_event_sample *E = (perf_event_sample *)Buf;
> +
> +  if (E->header.type == PERF_RECORD_MMAP) {
> +    perf_event_mmap *E = (perf_event_mmap *)Buf;
> +    auto MapID = Maps.size();
> +    Maps.push_back({E->start, E->start + E->extent, E->filename});
> +
> +    // Clear out the current range if applicable.
> +    auto L = CurrentMaps.lower_bound(E->start);
> +    auto H = CurrentMaps.upper_bound(E->start + E->extent);
> +    CurrentMaps.erase(L, H);
> +
> +    CurrentMaps.insert({E->start, MapID});
> +  }
> +  if (E->header.type == PERF_RECORD_MMAP2) {
> +    perf_event_mmap2 *E = (perf_event_mmap2 *)Buf;
> +    auto MapID = Maps.size();
> +    Maps.push_back({E->start, E->start + E->extent, E->filename});
> +
> +    // Clear out the current range if applicable.
> +    auto L = CurrentMaps.lower_bound(E->start);
> +    auto H = CurrentMaps.upper_bound(E->start + E->extent);
> +    CurrentMaps.erase(L, H);
> +
> +    CurrentMaps.insert({E->start, MapID});
> +  }
> +
> +  if (E->header.type != PERF_RECORD_SAMPLE)
> +    return &Buf[E->header.size];
> +
> +
> +  auto NewE = parseEvent(((unsigned char*)E) + sizeof(perf_event_header),
> +                         EventLayouts.begin()->second);
> +  auto EventID = NewE.id;
> +  auto PC = NewE.ip;
> +  auto MapID = std::prev(CurrentMaps.upper_bound(PC))->second;
> +
> +  assert(EventIDs.count(EventID));
> +  Events[MapID][PC][EventIDs[EventID]] += NewE.period;
> +
> +  TotalEvents[EventIDs[EventID]] += NewE.period;
> +  TotalEventsPerMap[MapID][EventIDs[EventID]] += NewE.period;
> +
> +  return &Buf[E->header.size];
> +}
> +
> +perf_event_sample PerfReader::parseEvent(unsigned char *Buf, uint64_t Layout) {
> +  perf_event_sample E;
> +  memset((char*)&E, 0, sizeof(E));
> +
> +  assert(Layout & PERF_SAMPLE_IP);
> +  assert(Layout & PERF_SAMPLE_PERIOD);
> +
> +  if (Layout & PERF_SAMPLE_IDENTIFIER)
> +    E.id = TakeU64(Buf);
> +  if (Layout & PERF_SAMPLE_IP)
> +    E.ip = TakeU64(Buf);
> +  if (Layout & PERF_SAMPLE_TID) {
> +    E.pid = TakeU32(Buf);
> +    E.tid = TakeU32(Buf);
> +  }
> +  if (Layout & PERF_SAMPLE_TIME)
> +    E.time = TakeU64(Buf);
> +  if (Layout & PERF_SAMPLE_ADDR)
> +    (void) TakeU64(Buf);
> +  if (Layout & PERF_SAMPLE_ID)
> +    E.id = TakeU64(Buf);
> +  if (Layout & PERF_SAMPLE_STREAM_ID)
> +    (void) TakeU64(Buf);
> +  if (Layout & PERF_SAMPLE_CPU)
> +    (void) TakeU64(Buf);
> +  if (Layout & PERF_SAMPLE_PERIOD)
> +    E.period = TakeU64(Buf);
> +
> +  return E;
> +}
> +
> +void PerfReader::emitFunctionStart(std::string &Name, bool First) {
> +  Lines.clear();
> +}
> +
> +void PerfReader::emitFunctionEnd(std::string &Name,
> +                                 std::map<const char *, uint64_t> &Counters) {
> +  auto *CounterDict = PyDict_New();
> +  for (auto &KV : Counters)
> +    PyDict_SetItemString(CounterDict, KV.first,
> +                         PyLong_FromUnsignedLongLong((unsigned long long)KV.second));
> +
> +  auto *LinesList = PyList_New(Lines.size());
> +  unsigned Idx = 0;
> +  for (auto *I : Lines)
> +    PyList_SetItem(LinesList, Idx++, I);
> +
> +  auto *FnDict = PyDict_New();
> +  PyDict_SetItemString(FnDict, "counters", CounterDict);
> +  PyDict_SetItemString(FnDict, "data", LinesList);
> +
> +  PyDict_SetItemString(Functions, Name.c_str(), FnDict);
> +}
> +
> +void PerfReader::emitLine(uint64_t PC,
> +                          std::map<const char *, uint64_t> *Counters,
> +                          const std::string &Text, bool First) {
> +  auto *CounterDict = PyDict_New();
> +  if (Counters)
> +    for (auto &KV : *Counters)
> +      PyDict_SetItemString(CounterDict, KV.first,
> +                           PyLong_FromUnsignedLongLong((unsigned long long)KV.second));
> +
> +  auto *Line = Py_BuildValue("[KNs]",
> +                             (unsigned long long) PC,
> +                             CounterDict,
> +                             (char*) Text.c_str());
> +  Lines.push_back(Line);
> +}
> +
> +void PerfReader::emitTopLevelCounters() {
> +  TopLevelCounters = PyDict_New();
> +  for (auto &KV : TotalEvents)
> +    PyDict_SetItemString(TopLevelCounters, KV.first,
> +                         PyLong_FromUnsignedLongLong((unsigned long long)KV.second));
> +
> +  Functions = PyDict_New();
> +}
> +
> +void PerfReader::emitMaps() {
> +  bool FirstSym = true;
> +  for (auto &KV : Events) {
> +    auto MapID = KV.first;
> +    auto &MapEvents = KV.second;
> +
> +    if (MapEvents.empty())
> +      continue;
> +
> +    if (MapID > Maps.size()) {
> +      // Something went badly wrong. Try and recover.
> +      continue;
> +    }
> +
> +    // Are there enough events here to bother with?
> +    bool AllUnderThreshold = true;
> +    for (auto &I : TotalEventsPerMap[MapID]) {
> +      auto Total = TotalEvents[I.first];
> +      // If a map contains more than 1% of some event, bother with it.
> +      if ((float)I.second / (float)Total > 0.01f) {
> +        AllUnderThreshold = false;
> +        break;
> +      }
> +    }
> +    if (AllUnderThreshold)
> +      continue;
> +
> +    NmOutput Syms(Nm);
> +    Syms.reset(&Maps[MapID]);
> +    auto Sym = Syms.begin();
> +
> +    auto Event = MapEvents.begin();
> +    while (Event != MapEvents.end() && Sym != Syms.end()) {
> +      auto PC = Event->first;
> +      if (PC < Sym->Start) {
> +        ++Event;
> +        continue;
> +      }
> +      while (Sym != Syms.end() && PC >= Sym->End)
> +        ++Sym;
> +      if (Sym == Syms.end() || Sym->Start > PC) {
> +        ++Event;
> +        continue;
> +      }
> +
> +      emitSymbol(*Sym++, Maps[MapID], Event, FirstSym);
> +      FirstSym = false;
> +    }
> +  }
> +}
> +
> +void PerfReader::emitSymbol(
> +    Symbol &Sym, Map &M,
> +    std::map<uint64_t, std::map<const char *, uint64_t>>::iterator &Event,
> +    bool FirstSym) {
> +  ObjdumpOutput Dump(Objdump);
> +  Dump.reset(&M, Sym.Start, Sym.End);
> +  Dump.next();
> +
> +  std::map<const char *, uint64_t> SymEvents;
> +
> +  emitFunctionStart(Sym.Name, FirstSym);
> +  for (uint64_t I = Sym.Start; I < Sym.End; I = Dump.next()) {
> +    auto PC = Event->first;
> +
> +    auto Text = Dump.getText();
> +    if (PC == I) {
> +      emitLine(I, &Event->second, Text, I == Sym.Start);
> +
> +      for (auto &KV : Event->second)
> +        SymEvents[KV.first] += KV.second;
> +
> +      ++Event;
> +    } else {
> +      emitLine(I, nullptr, Text, I == Sym.Start);
> +    }
> +  }
> +  emitFunctionEnd(Sym.Name, SymEvents);
> +}
> +
> +PyObject *PerfReader::complete() {
> +  auto *Obj = PyDict_New();
> +  PyDict_SetItemString(Obj, "counters", TopLevelCounters);
> +  PyDict_SetItemString(Obj, "functions", Functions);
> +  return Obj;
> +}
> +
> +#ifndef STANDALONE
> +static PyObject *cPerf_importPerf(PyObject *self, PyObject *args) {
> +  const char *Fname;
> +  const char *Nm = "nm";
> +  const char *Objdump = "objdump";
> +  if (!PyArg_ParseTuple(args, "s|ss", &Fname, &Nm, &Objdump))
> +    return NULL;
> +
> +  try {
> +    PerfReader P(Fname, Nm, Objdump);
> +    P.readHeader();
> +    P.readAttrs();
> +    P.readDataStream();
> +    P.emitTopLevelCounters();
> +    P.emitMaps();
> +    return P.complete();
> +  } catch (std::logic_error &E) {
> +    PyErr_SetString(PyExc_AssertionError, E.what());
> +    return NULL;
> +  } catch (std::runtime_error &E) {
> +    PyErr_SetString(PyExc_RuntimeError, E.what());
> +    return NULL;
> +  } catch (...) {
> +    PyErr_SetString(PyExc_RuntimeError, "Unknown error");
> +    return NULL;
> +  }
> +}
> +
> +static PyMethodDef cPerfMethods[] = {{"importPerf", cPerf_importPerf,
> +                                      METH_VARARGS,
> +                                      "Import perf.data from a filename"},
> +                                     {NULL, NULL, 0, NULL}};
> +
> +PyMODINIT_FUNC initcPerf(void) { (void)Py_InitModule("cPerf", cPerfMethods); }
> +
> +#else // STANDALONE
> +
> +int main(int argc, char **argv) {
> +  PerfReader P(argv[1], std::cout);
> +  P.readHeader();
> +  P.readAttrs();
> +  P.readDataStream();
> +  P.emitTopLevelCounters();
> +  P.emitMaps();
> +  P.complete();
> +}
> +
> +#endif
>
> Added: lnt/trunk/lnt/testing/profile/perf.py
> URL: http://llvm.org/viewvc/llvm-project/lnt/trunk/lnt/testing/profile/perf.py?rev=263560&view=auto
> ==============================================================================
> --- lnt/trunk/lnt/testing/profile/perf.py (added)
> +++ lnt/trunk/lnt/testing/profile/perf.py Tue Mar 15 11:52:20 2016
> @@ -0,0 +1,45 @@
> +
> +import json, os
> +from profile import ProfileImpl
> +from profilev1impl import ProfileV1
> +try:
> +    import cPerf
> +except:
> +    pass
> +
> +class LinuxPerfProfile(ProfileImpl):
> +    def __init__(self):
> +        pass
> +
> +    @staticmethod
> +    def checkFile(fn):
> +        return open(fn).read(8) == 'PERFILE2'
> +
> +    @staticmethod
> +    def deserialize(f, nm='nm', objdump='objdump', propagateExceptions=False):
> +        f = f.name
> +
> +        if os.path.getsize(f) == 0:
> +            # Empty file - exit early.
> +            return None
> +
> +        try:
> +            data = cPerf.importPerf(f, nm, objdump)
> +
> +            # Go through the data and convert counter values to percentages.
> +            for f in data['functions'].values():
> +                fc = f['counters']
> +                for l in f['data']:
> +                    for k,v in l[1].items():
> +                        l[1][k] = 100.0 * float(v) / fc[k]
> +                for k,v in fc.items():
> +                    fc[k] = 100.0 * v / data['counters'][k]
> +
> +            return ProfileV1(data)
> +
> +        except:
> +            if propagateExceptions:
> +                raise
> +            # FIXME: import warning!
> +            warning(traceback.format_exc())
> +            return None
>
> Modified: lnt/trunk/setup.py
> URL: http://llvm.org/viewvc/llvm-project/lnt/trunk/setup.py?rev=263560&r1=263559&r2=263560&view=diff
> ==============================================================================
> --- lnt/trunk/setup.py (original)
> +++ lnt/trunk/setup.py Tue Mar 15 11:52:20 2016
> @@ -1,7 +1,7 @@
>  import lnt
>  import os
>
> -from setuptools import setup, find_packages
> +from setuptools import setup, find_packages, Extension
>
>  # setuptools expects to be invoked from within the directory of setup.py, but it
>  # is nice to allow:
> @@ -9,6 +9,11 @@ from setuptools import setup, find_packa
>  # to work (for scripts, etc.)
>  os.chdir(os.path.dirname(os.path.abspath(__file__)))
>
> +cPerf = Extension('lnt.testing.profile.cPerf',
> +                  sources = ['lnt/testing/profile/cPerf.cpp'],
> +                  extra_compile_args = ['-std=c++11'],
> +                  optional = True)
> +
>  setup(
>      name = "LNT",
>      version = lnt.__version__,
> @@ -95,4 +100,6 @@ http://llvm.org/svn/llvm-project/lnt/tru
>                        'Flask-RESTful',
>                        'SQLAlchemy',
>                        'Flask'],
> +
> +    ext_modules = [cPerf],
>  )
>
> Modified: lnt/trunk/tests/lit.cfg
> URL: http://llvm.org/viewvc/llvm-project/lnt/trunk/tests/lit.cfg?rev=263560&r1=263559&r2=263560&view=diff
> ==============================================================================
> --- lnt/trunk/tests/lit.cfg (original)
> +++ lnt/trunk/tests/lit.cfg Tue Mar 15 11:52:20 2016
> @@ -2,6 +2,7 @@
>
>  import os
>  import platform
> +import glob
>
>  import lit.formats
>  import lit.util
> @@ -28,7 +29,11 @@ config.test_exec_root = 'test_run_tmp'
>  config.target_triple = None
>
>  src_root = os.path.join(config.test_source_root, '..')
> -config.environment['PYTHONPATH'] = src_root
> +try:
> +    build_root = glob.glob('%s/build/lib.*' % src_root)[0]
> +except:
> +    build_root = ''
> +config.environment['PYTHONPATH'] = '%s:%s' % (build_root, src_root)
>  # Don't generate .pyc files when running tests.
>  config.environment['PYTHONDONTWRITEBYTECODE'] = "1"
>
>
> Added: lnt/trunk/tests/testing/Inputs/fake-nm.py
> URL: http://llvm.org/viewvc/llvm-project/lnt/trunk/tests/testing/Inputs/fake-nm.py?rev=263560&view=auto
> ==============================================================================
> --- lnt/trunk/tests/testing/Inputs/fake-nm.py (added)
> +++ lnt/trunk/tests/testing/Inputs/fake-nm.py Tue Mar 15 11:52:20 2016
> @@ -0,0 +1,3 @@
> +import os, sys
> +
> +sys.stdout.write(open(sys.argv[1]).read())
>
> Added: lnt/trunk/tests/testing/Inputs/fake-objdump.py
> URL: http://llvm.org/viewvc/llvm-project/lnt/trunk/tests/testing/Inputs/fake-objdump.py?rev=263560&view=auto
> ==============================================================================
> --- lnt/trunk/tests/testing/Inputs/fake-objdump.py (added)
> +++ lnt/trunk/tests/testing/Inputs/fake-objdump.py Tue Mar 15 11:52:20 2016
> @@ -0,0 +1,11 @@
> +import os, sys
> +
> +for a in sys.argv:
> +    if a.startswith('--start-address'):
> +        addr = a.split('=')[1]
> +
> +        fname = '%s.%s.out' % (sys.argv[1], addr)
> +        sys.stdout.write(open(fname).read())
> +        sys.exit(0)
> +
> +sys.exit(1)
>
> Added: lnt/trunk/tests/testing/Inputs/fib-aarch64.nm.out
> URL: http://llvm.org/viewvc/llvm-project/lnt/trunk/tests/testing/Inputs/fib-aarch64.nm.out?rev=263560&view=auto
> ==============================================================================
> --- lnt/trunk/tests/testing/Inputs/fib-aarch64.nm.out (added)
> +++ lnt/trunk/tests/testing/Inputs/fib-aarch64.nm.out Tue Mar 15 11:52:20 2016
> @@ -0,0 +1,31 @@
> +00000000004017d8 00000000000001e0 d _DYNAMIC
> +00000000004019b8 0000000000000040 d _GLOBAL_OFFSET_TABLE_
> +00000000004007c4 0000000000000004 R _IO_stdin_used
> +00000000004007cc r __FRAME_END__
> +0000000000401a18 d __JCR_END__
> +0000000000401a18 d __JCR_LIST__
> +0000000000401a20 d __TMC_END__
> +0000000000401a20 d __TMC_LIST__
> +0000000000401a30 A __bss_start
> +0000000000401a08 D __data_start
> +0000000000400658 t __do_global_dtors_aux
> +0000000000401a20 t __do_global_dtors_aux_fini_array_entry
> +0000000000401a10 d __dso_handle
> +0000000000401a28 t __frame_dummy_init_array_entry
> +0000000000401a30 t __init_array_end
> +0000000000401a28 t __init_array_start
> +00000000004007b0 0000000000000004 T __libc_csu_fini
> +0000000000400738 0000000000000078 T __libc_csu_init
> +0000000000401a30 A _edata
> +0000000000401a34 A _end
> +00000000004007b4 T _fini
> +00000000004004f0 T _init
> +0000000000400578 T _start
> +00000000004005c0 0000000000000014 t call_weak_fn
> +0000000000401a30 0000000000000001 b completed.7233
> +0000000000401a08 W data_start
> +00000000004005d8 t deregister_tm_clones
> +00000000004006c8 0000000000000038 T fib
> +0000000000400688 t frame_dummy
> +0000000000400700 0000000000000038 T main
> +0000000000400618 t register_tm_clones
>
> Added: lnt/trunk/tests/testing/Inputs/fib-aarch64.objdump.0x4006c8.out
> URL: http://llvm.org/viewvc/llvm-project/lnt/trunk/tests/testing/Inputs/fib-aarch64.objdump.0x4006c8.out?rev=263560&view=auto
> ==============================================================================
> --- lnt/trunk/tests/testing/Inputs/fib-aarch64.objdump.0x4006c8.out (added)
> +++ lnt/trunk/tests/testing/Inputs/fib-aarch64.objdump.0x4006c8.out Tue Mar 15 11:52:20 2016
> @@ -0,0 +1,21 @@
> +
> +fib:     file format elf64-littleaarch64
> +
> +
> +Disassembly of section .text:
> +
> +00000000004006c8 <fib>:
> +  4006c8:      a9be4ff4        stp     x20, x19, [sp,#-32]!
> +  4006cc:      a9017bfd        stp     x29, x30, [sp,#16]
> +  4006d0:      910043fd        add     x29, sp, #0x10
> +  4006d4:      71000813        subs    w19, w0, #0x2
> +  4006d8:      540000eb        b.lt    4006f4 <fib+0x2c>
> +  4006dc:      51000400        sub     w0, w0, #0x1
> +  4006e0:      97fffffa        bl      4006c8 <fib>
> +  4006e4:      2a0003f4        mov     w20, w0
> +  4006e8:      2a1303e0        mov     w0, w19
> +  4006ec:      97fffff7        bl      4006c8 <fib>
> +  4006f0:      0b140000        add     w0, w0, w20
> +  4006f4:      a9417bfd        ldp     x29, x30, [sp,#16]
> +  4006f8:      a8c24ff4        ldp     x20, x19, [sp],#32
> +  4006fc:      d65f03c0        ret
>
> Added: lnt/trunk/tests/testing/Inputs/fib-aarch64.perf_data
> URL: http://llvm.org/viewvc/llvm-project/lnt/trunk/tests/testing/Inputs/fib-aarch64.perf_data?rev=263560&view=auto
> ==============================================================================
> Binary files lnt/trunk/tests/testing/Inputs/fib-aarch64.perf_data (added) and lnt/trunk/tests/testing/Inputs/fib-aarch64.perf_data Tue Mar 15 11:52:20 2016 differ
>
> Added: lnt/trunk/tests/testing/Inputs/fib2-aarch64.nm.out
> URL: http://llvm.org/viewvc/llvm-project/lnt/trunk/tests/testing/Inputs/fib2-aarch64.nm.out?rev=263560&view=auto
> ==============================================================================
> --- lnt/trunk/tests/testing/Inputs/fib2-aarch64.nm.out (added)
> +++ lnt/trunk/tests/testing/Inputs/fib2-aarch64.nm.out Tue Mar 15 11:52:20 2016
> @@ -0,0 +1,31 @@
> +00000000004017d8 00000000000001e0 d _DYNAMIC
> +00000000004019b8 0000000000000040 d _GLOBAL_OFFSET_TABLE_
> +00000000004007c4 0000000000000004 R _IO_stdin_used
> +00000000004007cc r __FRAME_END__
> +0000000000401a18 d __JCR_END__
> +0000000000401a18 d __JCR_LIST__
> +0000000000401a20 d __TMC_END__
> +0000000000401a20 d __TMC_LIST__
> +0000000000401a30 A __bss_start
> +0000000000401a08 D __data_start
> +0000000000400658 t __do_global_dtors_aux
> +0000000000401a20 t __do_global_dtors_aux_fini_array_entry
> +0000000000401a10 d __dso_handle
> +0000000000401a28 t __frame_dummy_init_array_entry
> +0000000000401a30 t __init_array_end
> +0000000000401a28 t __init_array_start
> +00000000004007b0 0000000000000004 T __libc_csu_fini
> +0000000000400738 0000000000000078 T __libc_csu_init
> +0000000000401a30 A _edata
> +0000000000401a34 A _end
> +00000000004007b4 T _fini
> +00000000004004f0 T _init
> +0000000000400578 T _start
> +00000000004005c0 0000000000000014 t call_weak_fn
> +0000000000401a30 0000000000000001 b completed.7233
> +0000000000401a08 W data_start
> +00000000004005d8 t deregister_tm_clones
> +00000000004006c8 0000000000000038 T fib
> +0000000000400688 t frame_dummy
> +0000000000400700 0000000000000038 T main
> +0000000000400618 t register_tm_clones
>
> Added: lnt/trunk/tests/testing/Inputs/fib2-aarch64.objdump.0x4006c8.out
> URL: http://llvm.org/viewvc/llvm-project/lnt/trunk/tests/testing/Inputs/fib2-aarch64.objdump.0x4006c8.out?rev=263560&view=auto
> ==============================================================================
> --- lnt/trunk/tests/testing/Inputs/fib2-aarch64.objdump.0x4006c8.out (added)
> +++ lnt/trunk/tests/testing/Inputs/fib2-aarch64.objdump.0x4006c8.out Tue Mar 15 11:52:20 2016
> @@ -0,0 +1,21 @@
> +
> +fib:     file format elf64-littleaarch64
> +
> +
> +Disassembly of section .text:
> +
> +00000000004006c8 <fib>:
> +  4006c8:      a9be4ff4        stp     x20, x19, [sp,#-32]!
> +  4006cc:      a9017bfd        stp     x29, x30, [sp,#16]
> +  4006d0:      910043fd        add     x29, sp, #0x10
> +  4006d4:      71000813        subs    w19, w0, #0x2
> +  4006d8:      540000eb        b.lt    4006f4 <fib+0x2c>
> +  4006dc:      51000400        sub     w0, w0, #0x1
> +  4006e0:      97fffffa        bl      4006c8 <fib>
> +  4006e4:      2a0003f4        mov     w20, w0
> +  4006e8:      2a1303e0        mov     w0, w19
> +  4006ec:      97fffff7        bl      4006c8 <fib>
> +  4006f0:      0b140000        add     w0, w0, w20
> +  4006f4:      a9417bfd        ldp     x29, x30, [sp,#16]
> +  4006f8:      a8c24ff4        ldp     x20, x19, [sp],#32
> +  4006fc:      d65f03c0        ret
>
> Added: lnt/trunk/tests/testing/Inputs/fib2-aarch64.perf_data
> URL: http://llvm.org/viewvc/llvm-project/lnt/trunk/tests/testing/Inputs/fib2-aarch64.perf_data?rev=263560&view=auto
> ==============================================================================
> Binary files lnt/trunk/tests/testing/Inputs/fib2-aarch64.perf_data (added) and lnt/trunk/tests/testing/Inputs/fib2-aarch64.perf_data Tue Mar 15 11:52:20 2016 differ
>
> Added: lnt/trunk/tests/testing/cPerf.py
> URL: http://llvm.org/viewvc/llvm-project/lnt/trunk/tests/testing/cPerf.py?rev=263560&view=auto
> ==============================================================================
> --- lnt/trunk/tests/testing/cPerf.py (added)
> +++ lnt/trunk/tests/testing/cPerf.py Tue Mar 15 11:52:20 2016
> @@ -0,0 +1,78 @@
> +# RUN: python %s
> +import unittest, sys, os, tempfile, time, threading
> +
> +try:
> +    import lnt.testing.profile.cPerf as cPerf
> +except:
> +    # No tests to run if cPerf is not available
> +    sys.exit(0)
> +
> +from lnt.testing.profile.perf import LinuxPerfProfile
> +
> +class CPerfTest(unittest.TestCase):
> +    def setUp(self):
> +        self.inputs = os.path.join(os.path.dirname(os.path.abspath(__file__)),
> +                                   'Inputs')
> +        self.fake_nm = 'python %s/fake-nm.py' % self.inputs
> +
> +        self.expected_data = {
> +            'fib-aarch64': {u'functions': {u'fib': {u'data': [[4196040, {u'cycles': 22.476272172208624}, u'\ta9be4ff4 \tstp\tx20, x19, [sp,#-32]!'], [4196044, {u'cycles': 20.81533649797271}, u'\ta9017bfd \tstp\tx29, x30, [sp,#16]'], [4196048, {}, u'\t910043fd \tadd\tx29, sp, #0x10'], [4196052, {}, u'\t71000813 \tsubs\tw19, w0, #0x2'], [4196056, {}, u'\t540000eb \tb.lt\t4006f4 <fib+0x2c>'], [4196060, {u'cycles': 10.065491723992467}, u'\t51000400 \tsub\tw0, w0, #0x1'], [4196064, {}, u'\t97fffffa \tbl\t4006c8 <fib>'], [4196068, {u'cycles': 5.858831022967777}, u'\t2a0003f4 \tmov\tw20, w0'], [4196072, {}, u'\t2a1303e0 \tmov\tw0, w19'], [4196076, {}, u'\t97fffff7 \tbl\t4006c8 <fib>'], [4196080, {u'cycles': 7.57924022814841}, u'\t0b140000 \tadd\tw0, w0, w20'], [4196084, {u'cycles': 19.240308514111305}, u'\ta9417bfd \tldp\tx29, x30, [sp,#16]'], [4196088, {u'cycles': 13.964519840598708}, u'\ta8c24ff4 \tldp\tx20, x19, [sp],#32'], [4196092, {}, u'\td65f03c0 \tret']], u'counters': {u'cycles': 9
>  9.77243187496647}}}, u'counters': {u'cycles': 240949386}},
> +            'fib2-aarch64': {u'functions': {u'fib': {u'data': [[4196040, {u'cycles': 23.48637833693693, u'branch-misses': 21.904846340904687, u'cache-misses': 37.4486921529175}, u'\ta9be4ff4 \tstp\tx20, x19, [sp,#-32]!'], [4196044, {u'cycles': 20.34001001463117, u'branch-misses': 2.6443747907452115, u'cache-misses': 17.08651911468813}, u'\ta9017bfd \tstp\tx29, x30, [sp,#16]'], [4196048, {}, u'\t910043fd \tadd\tx29, sp, #0x10'], [4196052, {}, u'\t71000813 \tsubs\tw19, w0, #0x2'], [4196056, {}, u'\t540000eb \tb.lt\t4006f4 <fib+0x2c>'], [4196060, {u'cycles': 9.787981545863996, u'branch-misses': 30.264575146698622, u'cache-misses': 20.69215291750503}, u'\t51000400 \tsub\tw0, w0, #0x1'], [4196064, {}, u'\t97fffffa \tbl\t4006c8 <fib>'], [4196068, {u'cycles': 7.702120542412432, u'branch-misses': 0.11195131191739062, u'cache-misses': 2.3621730382293764}, u'\t2a0003f4 \tmov\tw20, w0'], [4196072, {}, u'\t2a1303e0 \tmov\tw0, w19'], [4196076, {}, u'\t97fffff7 \tbl\t4006c8 <fib>'], [4196080, {u'
>  cycles': 7.362266427937867, u'branch-misses': 19.03265916580028, u'cache-misses': 3.8229376257545273}, u'\t0b140000 \tadd\tw0, w0, w20'], [4196084, {u'cycles': 18.387547715628735, u'branch-misses': 4.9891297644011345, u'cache-misses': 7.553319919517103}, u'\ta9417bfd \tldp\tx29, x30, [sp,#16]'], [4196088, {u'cycles': 12.93369541658887, u'branch-misses': 21.05246347953268, u'cache-misses': 11.03420523138833}, u'\ta8c24ff4 \tldp\tx20, x19, [sp],#32'], [4196092, {}, u'\td65f03c0 \tret']], u'counters': {u'cycles': 99.78902404723429, u'branch-misses': 99.7405382129432, u'cache-misses': 75.18000847098688}}}, u'counters': {u'cycles': 243618286, u'branch-misses': 1820692, u'cache-misses': 33054}}
> +            }
> +
> +
> +
> +    def _getNm(self, perf_data_fname):
> +        stub = perf_data_fname.rsplit('.perf_data', 1)[0]
> +        return 'python %s/fake-nm.py %s.nm.out' % (self.inputs, stub)
> +
> +    def _getObjdump(self, perf_data_fname):
> +        stub = perf_data_fname.rsplit('.perf_data', 1)[0]
> +        return 'python %s/fake-objdump.py %s.objdump' % (self.inputs, stub)
> +
> +    def _getInput(self, fname):
> +        return os.path.join(self.inputs, fname)
> +
> +    def test_check_file(self):
> +        self.assertTrue(LinuxPerfProfile.checkFile(self._getInput('fib-aarch64.perf_data')))
> +
> +    def test_aarch64_fib(self):
> +       perf_data = self._getInput('fib-aarch64.perf_data')
> +       p = LinuxPerfProfile.deserialize(open(perf_data),
> +                                        nm=self._getNm(perf_data),
> +                                        objdump=self._getObjdump(perf_data),
> +                                        propagateExceptions=True)
> +
> +       self.assertEqual(p.data, self.expected_data['fib-aarch64'])
> +
> +    def test_aarch64_fib2(self):
> +       perf_data = self._getInput('fib2-aarch64.perf_data')
> +       p = LinuxPerfProfile.deserialize(open(perf_data),
> +                                        nm=self._getNm(perf_data),
> +                                        objdump=self._getObjdump(perf_data),
> +                                        propagateExceptions=True)
> +
> +       self.assertEqual(p.data, self.expected_data['fib2-aarch64'])
> +
> +    def test_random_guff(self):
> +        # Create complete rubbish and throw it at cPerf, expecting an
> +        # AssertionError.
> +        data = '6492gbiajng295akgjowj210441'
> +        with tempfile.NamedTemporaryFile() as fd:
> +            open(fd.name, 'w').write(data)
> +            with self.assertRaises(AssertionError):
> +                LinuxPerfProfile.deserialize(open(fd.name),
> +                                             propagateExceptions=True)
> +
> +    def test_random_guff2(self):
> +        # Create complete rubbish and throw it at cPerf, expecting an
> +        # AssertionError. This version contains the correct magic number.
> +        data = 'PERFILE28620k hshjsjhs&6362kkjh25090nnjh'
> +        with tempfile.NamedTemporaryFile() as fd:
> +            open(fd.name, 'w').write(data)
> +            with self.assertRaises(AssertionError):
> +                LinuxPerfProfile.deserialize(open(fd.name),
> +                                             propagateExceptions=True)
> +
> +if __name__ == '__main__':
> +    unittest.main(argv=[sys.argv[0], ])
>
>
> _______________________________________________
> llvm-commits mailing list
> llvm-commits at lists.llvm.org
> http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-commits


More information about the llvm-commits mailing list