brintos

brintos / llvm-project-archived public Read only

0
0
Text · 8.0 KiB · da50642 Raw
236 lines · cpp
1//===-- profile_collector_test.cpp ----------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// This file is a part of XRay, a function call tracing system.10//11//===----------------------------------------------------------------------===//12#include "gtest/gtest.h"13 14#include "xray_profile_collector.h"15#include "xray_profiling_flags.h"16#include <cstdint>17#include <cstring>18#include <memory>19#include <thread>20#include <utility>21#include <vector>22 23namespace __xray {24namespace {25 26static constexpr auto kHeaderSize = 16u;27 28constexpr uptr ExpectedProfilingVersion = 0x20180424;29 30struct ExpectedProfilingFileHeader {31  const u64 MagicBytes = 0x7872617970726f66; // Identifier for XRay profiling32                                             // files 'xrayprof' in hex.33  const u64 Version = ExpectedProfilingVersion;34  u64 Timestamp = 0;35  u64 PID = 0;36};37 38void ValidateFileHeaderBlock(XRayBuffer B) {39  ASSERT_NE(static_cast<const void *>(B.Data), nullptr);40  ASSERT_EQ(B.Size, sizeof(ExpectedProfilingFileHeader));41  alignas(ExpectedProfilingFileHeader)42      std::byte FileHeaderStorage[sizeof(ExpectedProfilingFileHeader)];43  ExpectedProfilingFileHeader ExpectedHeader;44  std::memcpy(&FileHeaderStorage, B.Data, B.Size);45  auto &FileHeader =46      *reinterpret_cast<ExpectedProfilingFileHeader *>(&FileHeaderStorage);47  ASSERT_EQ(ExpectedHeader.MagicBytes, FileHeader.MagicBytes);48  ASSERT_EQ(ExpectedHeader.Version, FileHeader.Version);49}50 51void ValidateBlock(XRayBuffer B) {52  profilingFlags()->setDefaults();53  ASSERT_NE(static_cast<const void *>(B.Data), nullptr);54  ASSERT_NE(B.Size, 0u);55  ASSERT_GE(B.Size, kHeaderSize);56  // We look at the block size, the block number, and the thread ID to ensure57  // that none of them are zero (or that the header data is laid out as we58  // expect).59  char LocalBuffer[kHeaderSize] = {};60  internal_memcpy(LocalBuffer, B.Data, kHeaderSize);61  u32 BlockSize = 0;62  u32 BlockNumber = 0;63  u64 ThreadId = 0;64  internal_memcpy(&BlockSize, LocalBuffer, sizeof(u32));65  internal_memcpy(&BlockNumber, LocalBuffer + sizeof(u32), sizeof(u32));66  internal_memcpy(&ThreadId, LocalBuffer + (2 * sizeof(u32)), sizeof(u64));67  ASSERT_NE(BlockSize, 0u);68  ASSERT_GE(BlockNumber, 0u);69  ASSERT_NE(ThreadId, 0u);70}71 72std::tuple<u32, u32, u64> ParseBlockHeader(XRayBuffer B) {73  char LocalBuffer[kHeaderSize] = {};74  internal_memcpy(LocalBuffer, B.Data, kHeaderSize);75  u32 BlockSize = 0;76  u32 BlockNumber = 0;77  u64 ThreadId = 0;78  internal_memcpy(&BlockSize, LocalBuffer, sizeof(u32));79  internal_memcpy(&BlockNumber, LocalBuffer + sizeof(u32), sizeof(u32));80  internal_memcpy(&ThreadId, LocalBuffer + (2 * sizeof(u32)), sizeof(u64));81  return std::make_tuple(BlockSize, BlockNumber, ThreadId);82}83 84struct Profile {85  int64_t CallCount;86  int64_t CumulativeLocalTime;87  std::vector<int32_t> Path;88};89 90std::tuple<Profile, const char *> ParseProfile(const char *P) {91  Profile Result;92  // Read the path first, until we find a sentinel 0.93  int32_t F;94  do {95    internal_memcpy(&F, P, sizeof(int32_t));96    P += sizeof(int32_t);97    Result.Path.push_back(F);98  } while (F != 0);99 100  // Then read the CallCount.101  internal_memcpy(&Result.CallCount, P, sizeof(int64_t));102  P += sizeof(int64_t);103 104  // Then read the CumulativeLocalTime.105  internal_memcpy(&Result.CumulativeLocalTime, P, sizeof(int64_t));106  P += sizeof(int64_t);107  return std::make_tuple(std::move(Result), P);108}109 110TEST(profileCollectorServiceTest, PostSerializeCollect) {111  profilingFlags()->setDefaults();112  bool Success = false;113  BufferQueue BQ(profilingFlags()->per_thread_allocator_max,114                 profilingFlags()->buffers_max, Success);115  ASSERT_EQ(Success, true);116  FunctionCallTrie::Allocators::Buffers Buffers;117  ASSERT_EQ(BQ.getBuffer(Buffers.NodeBuffer), BufferQueue::ErrorCode::Ok);118  ASSERT_EQ(BQ.getBuffer(Buffers.RootsBuffer), BufferQueue::ErrorCode::Ok);119  ASSERT_EQ(BQ.getBuffer(Buffers.ShadowStackBuffer),120            BufferQueue::ErrorCode::Ok);121  ASSERT_EQ(BQ.getBuffer(Buffers.NodeIdPairBuffer), BufferQueue::ErrorCode::Ok);122  auto Allocators = FunctionCallTrie::InitAllocatorsFromBuffers(Buffers);123  FunctionCallTrie T(Allocators);124 125  // Populate the trie with some data.126  T.enterFunction(1, 1, 0);127  T.enterFunction(2, 2, 0);128  T.exitFunction(2, 3, 0);129  T.exitFunction(1, 4, 0);130 131  // Reset the collector data structures.132  profileCollectorService::reset();133 134  // Then we post the data to the global profile collector service.135  profileCollectorService::post(&BQ, std::move(T), std::move(Allocators),136                                std::move(Buffers), 1);137 138  // Then we serialize the data.139  profileCollectorService::serialize();140 141  // Then we go through two buffers to see whether we're getting the data we142  // expect. The first block must always be as large as a file header, which143  // will have a fixed size.144  auto B = profileCollectorService::nextBuffer({nullptr, 0});145  ValidateFileHeaderBlock(B);146 147  B = profileCollectorService::nextBuffer(B);148  ValidateBlock(B);149  u32 BlockSize;150  u32 BlockNum;151  u64 ThreadId;152  std::tie(BlockSize, BlockNum, ThreadId) = ParseBlockHeader(B);153 154  // We look at the serialized buffer to see whether the Trie we're expecting155  // to see is there.156  auto DStart = static_cast<const char *>(B.Data) + kHeaderSize;157  std::vector<char> D(DStart, DStart + BlockSize);158  B = profileCollectorService::nextBuffer(B);159  ASSERT_EQ(B.Data, nullptr);160  ASSERT_EQ(B.Size, 0u);161 162  Profile Profile1, Profile2;163  auto P = static_cast<const char *>(D.data());164  std::tie(Profile1, P) = ParseProfile(P);165  std::tie(Profile2, P) = ParseProfile(P);166 167  ASSERT_NE(Profile1.Path.size(), Profile2.Path.size());168  auto &P1 = Profile1.Path.size() < Profile2.Path.size() ? Profile2 : Profile1;169  auto &P2 = Profile1.Path.size() < Profile2.Path.size() ? Profile1 : Profile2;170  std::vector<int32_t> P1Expected = {2, 1, 0};171  std::vector<int32_t> P2Expected = {1, 0};172  ASSERT_EQ(P1.Path.size(), P1Expected.size());173  ASSERT_EQ(P2.Path.size(), P2Expected.size());174  ASSERT_EQ(P1.Path, P1Expected);175  ASSERT_EQ(P2.Path, P2Expected);176}177 178// We break out a function that will be run in multiple threads, one that will179// use a thread local allocator, and will post the FunctionCallTrie to the180// profileCollectorService. This simulates what the threads being profiled would181// be doing anyway, but through the XRay logging implementation.182void threadProcessing() {183  static bool Success = false;184  static BufferQueue BQ(profilingFlags()->per_thread_allocator_max,185                        profilingFlags()->buffers_max, Success);186  thread_local FunctionCallTrie::Allocators::Buffers Buffers = [] {187    FunctionCallTrie::Allocators::Buffers B;188    BQ.getBuffer(B.NodeBuffer);189    BQ.getBuffer(B.RootsBuffer);190    BQ.getBuffer(B.ShadowStackBuffer);191    BQ.getBuffer(B.NodeIdPairBuffer);192    return B;193  }();194 195  thread_local auto Allocators =196      FunctionCallTrie::InitAllocatorsFromBuffers(Buffers);197 198  FunctionCallTrie T(Allocators);199 200  T.enterFunction(1, 1, 0);201  T.enterFunction(2, 2, 0);202  T.exitFunction(2, 3, 0);203  T.exitFunction(1, 4, 0);204 205  profileCollectorService::post(&BQ, std::move(T), std::move(Allocators),206                                std::move(Buffers), GetTid());207}208 209TEST(profileCollectorServiceTest, PostSerializeCollectMultipleThread) {210  profilingFlags()->setDefaults();211 212  profileCollectorService::reset();213 214  std::thread t1(threadProcessing);215  std::thread t2(threadProcessing);216 217  t1.join();218  t2.join();219 220  // At this point, t1 and t2 are already done with what they were doing.221  profileCollectorService::serialize();222 223  // Ensure that we see two buffers.224  auto B = profileCollectorService::nextBuffer({nullptr, 0});225  ValidateFileHeaderBlock(B);226 227  B = profileCollectorService::nextBuffer(B);228  ValidateBlock(B);229 230  B = profileCollectorService::nextBuffer(B);231  ValidateBlock(B);232}233 234} // namespace235} // namespace __xray236