//===-- SourcePrinter.cpp -  source interleaving utilities ----------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "llvm/DebugInfo/BTF/BTFContext.h"
#include "llvm/ObjectYAML/YAML.h"
#include "llvm/ObjectYAML/yaml2obj.h"
#include "llvm/Support/SwapByteOrder.h"
#include "llvm/Testing/Support/Error.h"

using namespace llvm;
using namespace llvm::object;

#define LC(Line, Col) ((Line << 10u) | Col)
#define ASSERT_SUCCEEDED(E) ASSERT_THAT_ERROR((E), Succeeded())

const char BTFEndOfData[] =
    "error while reading .BTF section: unexpected end of data";
const char BTFExtEndOfData[] =
    "error while reading .BTF.ext section: unexpected end of data";

static raw_ostream &operator<<(raw_ostream &OS, const yaml::BinaryRef &Ref) {
  Ref.writeAsHex(OS);
  return OS;
}

template <typename T>
static yaml::BinaryRef makeBinRef(const T *Ptr, size_t Size = sizeof(T)) {
  return yaml::BinaryRef(ArrayRef<uint8_t>((const uint8_t *)Ptr, Size));
}

namespace {
// This is a mockup for an ELF file containing .BTF and .BTF.ext sections.
// Binary content of these sections corresponds to the value of
// MockData1::BTF and MockData1::Ext fields.
//
// The yaml::yaml2ObjectFile() is used to generate actual ELF,
// see MockData1::makeObj().
//
// The `BTF` and `Ext` fields are initialized with correct values
// valid for a small example with a few sections, fields could be
// modified before a call to `makeObj()` to test parser with invalid
// input, etc.

struct MockData1 {
// Use "pragma pack" to model .BTF & .BTF.ext sections content using
// 'struct' objects. This pragma is supported by CLANG, GCC & MSVC,
// which matters for LLVM CI.
#pragma pack(push, 1)
  struct B {
    BTF::Header Header = {};
    // No types.
    struct S {
      char Foo[4] = "foo";
      char Bar[4] = "bar";
      char Buz[4] = "buz";
      char Line1[11] = "first line";
      char Line2[12] = "second line";
      char File1[4] = "a.c";
      char File2[4] = "b.c";
    } Strings;

    B() {
      Header.Magic = BTF::MAGIC;
      Header.Version = 1;
      Header.HdrLen = sizeof(Header);
      Header.StrOff = offsetof(B, Strings) - sizeof(Header);
      Header.StrLen = sizeof(Strings);
    }
  } BTF;

  struct E {
    BTF::ExtHeader Header = {};
    // No func info.
    struct {
      uint32_t LineRecSize = sizeof(BTF::BPFLineInfo);
      struct {
        BTF::SecLineInfo Sec = {offsetof(B::S, Foo), 2};
        BTF::BPFLineInfo Lines[2] = {
            {16, offsetof(B::S, File1), offsetof(B::S, Line1), LC(7, 1)},
            {32, offsetof(B::S, File1), offsetof(B::S, Line2), LC(14, 5)},
        };
      } Foo;
      struct {
        BTF::SecLineInfo Sec = {offsetof(B::S, Bar), 1};
        BTF::BPFLineInfo Lines[1] = {
            {0, offsetof(B::S, File2), offsetof(B::S, Line1), LC(42, 4)},
        };
      } Bar;
    } Lines;

    E() {
      Header.Magic = BTF::MAGIC;
      Header.Version = 1;
      Header.HdrLen = sizeof(Header);
      Header.LineInfoOff = offsetof(E, Lines) - sizeof(Header);
      Header.LineInfoLen = sizeof(Lines);
    }
  } Ext;
#pragma pack(pop)

  int BTFSectionLen = sizeof(BTF);
  int ExtSectionLen = sizeof(Ext);

  SmallString<0> Storage;
  std::unique_ptr<ObjectFile> Obj;

  ObjectFile &makeObj() {
    std::string Buffer;
    raw_string_ostream Yaml(Buffer);
    Yaml << R"(
!ELF
FileHeader:
  Class:    ELFCLASS64)";
    if (sys::IsBigEndianHost)
      Yaml << "\n  Data:     ELFDATA2MSB";
    else
      Yaml << "\n  Data:     ELFDATA2LSB";
    Yaml << R"(
  Type:     ET_REL
  Machine:  EM_BPF
Sections:
  - Name:     foo
    Type:     SHT_PROGBITS
    Size:     0x0
  - Name:     bar
    Type:     SHT_PROGBITS
    Size:     0x0)";

    if (BTFSectionLen >= 0)
      Yaml << R"(
  - Name:     .BTF
    Type:     SHT_PROGBITS
    Content: )"
           << makeBinRef(&BTF, BTFSectionLen);

    if (ExtSectionLen >= 0)
      Yaml << R"(
  - Name:     .BTF.ext
    Type:     SHT_PROGBITS
    Content: )"
           << makeBinRef(&Ext, ExtSectionLen);

    Obj = yaml::yaml2ObjectFile(Storage, Buffer,
                                [](const Twine &Err) { errs() << Err; });
    return *Obj;
  }
};

TEST(BTFParserTest, simpleCorrectInput) {
  BTFParser BTF;
  MockData1 Mock;
  Error Err = BTF.parse(Mock.makeObj());
  EXPECT_FALSE(Err);

  EXPECT_EQ(BTF.findString(offsetof(MockData1::B::S, Foo)), "foo");
  EXPECT_EQ(BTF.findString(offsetof(MockData1::B::S, Bar)), "bar");
  EXPECT_EQ(BTF.findString(offsetof(MockData1::B::S, Line1)), "first line");
  EXPECT_EQ(BTF.findString(offsetof(MockData1::B::S, Line2)), "second line");
  EXPECT_EQ(BTF.findString(offsetof(MockData1::B::S, File1)), "a.c");
  EXPECT_EQ(BTF.findString(offsetof(MockData1::B::S, File2)), "b.c");

  // Invalid offset.
  EXPECT_EQ(BTF.findString(sizeof(MockData1::B::S)), StringRef());

  const BTF::BPFLineInfo *I1 = BTF.findLineInfo({16, 1});
  ASSERT_TRUE(I1);
  EXPECT_EQ(I1->getLine(), 7u);
  EXPECT_EQ(I1->getCol(), 1u);
  EXPECT_EQ(BTF.findString(I1->FileNameOff), "a.c");
  EXPECT_EQ(BTF.findString(I1->LineOff), "first line");

  const BTF::BPFLineInfo *I2 = BTF.findLineInfo({32, 1});
  ASSERT_TRUE(I2);
  EXPECT_EQ(I2->getLine(), 14u);
  EXPECT_EQ(I2->getCol(), 5u);
  EXPECT_EQ(BTF.findString(I2->FileNameOff), "a.c");
  EXPECT_EQ(BTF.findString(I2->LineOff), "second line");

  const BTF::BPFLineInfo *I3 = BTF.findLineInfo({0, 2});
  ASSERT_TRUE(I3);
  EXPECT_EQ(I3->getLine(), 42u);
  EXPECT_EQ(I3->getCol(), 4u);
  EXPECT_EQ(BTF.findString(I3->FileNameOff), "b.c");
  EXPECT_EQ(BTF.findString(I3->LineOff), "first line");

  // No info for insn address.
  EXPECT_FALSE(BTF.findLineInfo({24, 1}));
  EXPECT_FALSE(BTF.findLineInfo({8, 2}));
  // No info for section number.
  EXPECT_FALSE(BTF.findLineInfo({16, 3}));
}

TEST(BTFParserTest, badSectionNameOffset) {
  BTFParser BTF;
  MockData1 Mock;
  // "foo" is section #1, corrupting it's name offset will make impossible
  // to match section name with section index when BTF is parsed.
  Mock.Ext.Lines.Foo.Sec.SecNameOff = 100500;
  Error Err = BTF.parse(Mock.makeObj());
  EXPECT_FALSE(Err);
  // "foo" line info should be corrupted.
  EXPECT_FALSE(BTF.findLineInfo({16, 1}));
  // "bar" line info should be ok.
  EXPECT_TRUE(BTF.findLineInfo({0, 2}));
}

// Keep this as macro to preserve line number info.
#define EXPECT_PARSE_ERROR(Mock, Message)                                      \
  do {                                                                         \
    BTFParser BTF;                                                             \
    EXPECT_THAT_ERROR(BTF.parse((Mock).makeObj()),                             \
                      FailedWithMessage(testing::HasSubstr(Message)));         \
  } while (false)

TEST(BTFParserTest, badBTFMagic) {
  MockData1 Mock;
  Mock.BTF.Header.Magic = 42;
  EXPECT_PARSE_ERROR(Mock, "invalid .BTF magic: 2a");
}

TEST(BTFParserTest, badBTFVersion) {
  MockData1 Mock;
  Mock.BTF.Header.Version = 42;
  EXPECT_PARSE_ERROR(Mock, "unsupported .BTF version: 42");
}

TEST(BTFParserTest, badBTFHdrLen) {
  MockData1 Mock;
  Mock.BTF.Header.HdrLen = 5;
  EXPECT_PARSE_ERROR(Mock, "unexpected .BTF header length: 5");
}

TEST(BTFParserTest, badBTFSectionLen) {
  MockData1 Mock1, Mock2;

  // Cut-off string section by one byte.
  Mock1.BTFSectionLen =
      offsetof(MockData1::B, Strings) + sizeof(MockData1::B::S) - 1;
  EXPECT_PARSE_ERROR(Mock1, "invalid .BTF section size");

  // Cut-off header.
  Mock2.BTFSectionLen = offsetof(BTF::Header, StrOff);
  EXPECT_PARSE_ERROR(Mock2, BTFEndOfData);
}

TEST(BTFParserTest, badBTFExtMagic) {
  MockData1 Mock;
  Mock.Ext.Header.Magic = 42;
  EXPECT_PARSE_ERROR(Mock, "invalid .BTF.ext magic: 2a");
}

TEST(BTFParserTest, badBTFExtVersion) {
  MockData1 Mock;
  Mock.Ext.Header.Version = 42;
  EXPECT_PARSE_ERROR(Mock, "unsupported .BTF.ext version: 42");
}

TEST(BTFParserTest, badBTFExtHdrLen) {
  MockData1 Mock1, Mock2;

  Mock1.Ext.Header.HdrLen = 5;
  EXPECT_PARSE_ERROR(Mock1, "unexpected .BTF.ext header length: 5");

  Mock2.Ext.Header.HdrLen = sizeof(Mock2.Ext);
  EXPECT_PARSE_ERROR(Mock2, BTFExtEndOfData);
}

TEST(BTFParserTest, badBTFExtSectionLen) {
  MockData1 Mock1, Mock2, Mock3;

  // Cut-off header before HdrLen.
  Mock1.ExtSectionLen = offsetof(BTF::ExtHeader, HdrLen);
  EXPECT_PARSE_ERROR(Mock1, BTFExtEndOfData);

  // Cut-off header before LineInfoLen.
  Mock2.ExtSectionLen = offsetof(BTF::ExtHeader, LineInfoLen);
  EXPECT_PARSE_ERROR(Mock2, BTFExtEndOfData);

  // Cut-off line-info section somewhere in the middle.
  Mock3.ExtSectionLen = offsetof(MockData1::E, Lines) + 4;
  EXPECT_PARSE_ERROR(Mock3, BTFExtEndOfData);
}

TEST(BTFParserTest, badBTFExtLineInfoRecSize) {
  MockData1 Mock1, Mock2;

  Mock1.Ext.Lines.LineRecSize = 2;
  EXPECT_PARSE_ERROR(Mock1, "unexpected .BTF.ext line info record length: 2");

  Mock2.Ext.Lines.LineRecSize = sizeof(Mock2.Ext.Lines.Foo.Lines[0]) + 1;
  EXPECT_PARSE_ERROR(Mock2, BTFExtEndOfData);
}

TEST(BTFParserTest, badBTFExtLineSectionName) {
  MockData1 Mock1;

  Mock1.Ext.Lines.Foo.Sec.SecNameOff = offsetof(MockData1::B::S, Buz);
  EXPECT_PARSE_ERROR(
      Mock1, "can't find section 'buz' while parsing .BTF.ext line info");
}

TEST(BTFParserTest, missingSections) {
  MockData1 Mock1, Mock2, Mock3;

  Mock1.BTFSectionLen = -1;
  EXPECT_PARSE_ERROR(Mock1, "can't find .BTF section");
  EXPECT_FALSE(BTFParser::hasBTFSections(Mock1.makeObj()));

  Mock2.ExtSectionLen = -1;
  EXPECT_PARSE_ERROR(Mock2, "can't find .BTF.ext section");
  EXPECT_FALSE(BTFParser::hasBTFSections(Mock2.makeObj()));

  EXPECT_TRUE(BTFParser::hasBTFSections(Mock3.makeObj()));
}

// Check that BTFParser instance is reset when BTFParser::parse() is
// called several times.
TEST(BTFParserTest, parserReset) {
  BTFParser BTF;
  MockData1 Mock1, Mock2;

  EXPECT_FALSE(BTF.parse(Mock1.makeObj()));
  EXPECT_TRUE(BTF.findLineInfo({16, 1}));
  EXPECT_TRUE(BTF.findLineInfo({0, 2}));

  // Break the reference to "bar" section name, thus making
  // information about "bar" line numbers unavailable.
  Mock2.Ext.Lines.Bar.Sec.SecNameOff = 100500;

  EXPECT_FALSE(BTF.parse(Mock2.makeObj()));
  EXPECT_TRUE(BTF.findLineInfo({16, 1}));
  // Make sure that "bar" no longer available (its index is 2).
  EXPECT_FALSE(BTF.findLineInfo({0, 2}));
}

TEST(BTFParserTest, btfContext) {
  MockData1 Mock;
  BTFParser BTF;
  std::unique_ptr<BTFContext> Ctx = BTFContext::create(Mock.makeObj());

  DILineInfo I1 = Ctx->getLineInfoForAddress({16, 1});
  EXPECT_EQ(I1.Line, 7u);
  EXPECT_EQ(I1.Column, 1u);
  EXPECT_EQ(I1.FileName, "a.c");
  EXPECT_EQ(I1.LineSource, "first line");

  DILineInfo I2 = Ctx->getLineInfoForAddress({24, 1});
  EXPECT_EQ(I2.Line, 0u);
  EXPECT_EQ(I2.Column, 0u);
  EXPECT_EQ(I2.FileName, DILineInfo::BadString);
  EXPECT_EQ(I2.LineSource, std::nullopt);
}

static uint32_t mkInfo(uint32_t Kind) { return Kind << 24; }

template <typename T> static void append(std::string &S, const T &What) {
  S.append((const char *)&What, sizeof(What));
}

class MockData2 {
  SmallString<0> ObjStorage;
  std::unique_ptr<ObjectFile> Obj;
  std::string Types;
  std::string Strings;
  std::string Relocs;
  std::string Lines;
  unsigned TotalTypes;
  int LastRelocSecIdx;
  unsigned NumRelocs;
  int LastLineSecIdx;
  unsigned NumLines;

public:
  MockData2() { reset(); }

  unsigned totalTypes() const { return TotalTypes; }

  uint32_t addString(StringRef S) {
    uint32_t Off = Strings.size();
    Strings.append(S.data(), S.size());
    Strings.append("\0", 1);
    return Off;
  };

  uint32_t addType(const BTF::CommonType &Tp) {
    append(Types, Tp);
    return ++TotalTypes;
  }

  template <typename T> void addTail(const T &Tp) { append(Types, Tp); }

  void resetTypes() {
    Types.resize(0);
    TotalTypes = 0;
  }

  void reset() {
    ObjStorage.clear();
    Types.resize(0);
    Strings.resize(0);
    Relocs.resize(0);
    Lines.resize(0);
    TotalTypes = 0;
    LastRelocSecIdx = -1;
    NumRelocs = 0;
    LastLineSecIdx = -1;
    NumLines = 0;
  }

  void finishRelocSec() {
    if (LastRelocSecIdx == -1)
      return;

    BTF::SecFieldReloc *SecInfo =
        (BTF::SecFieldReloc *)&Relocs[LastRelocSecIdx];
    SecInfo->NumFieldReloc = NumRelocs;
    LastRelocSecIdx = -1;
    NumRelocs = 0;
  }

  void finishLineSec() {
    if (LastLineSecIdx == -1)
      return;

    BTF::SecLineInfo *SecInfo = (BTF::SecLineInfo *)&Lines[LastLineSecIdx];
    SecInfo->NumLineInfo = NumLines;
    NumLines = 0;
    LastLineSecIdx = -1;
  }

  void addRelocSec(const BTF::SecFieldReloc &R) {
    finishRelocSec();
    LastRelocSecIdx = Relocs.size();
    append(Relocs, R);
  }

  void addReloc(const BTF::BPFFieldReloc &R) {
    append(Relocs, R);
    ++NumRelocs;
  }

  void addLinesSec(const BTF::SecLineInfo &R) {
    finishLineSec();
    LastLineSecIdx = Lines.size();
    append(Lines, R);
  }

  void addLine(const BTF::BPFLineInfo &R) {
    append(Lines, R);
    ++NumLines;
  }

  ObjectFile &makeObj() {
    finishRelocSec();
    finishLineSec();

    BTF::Header BTFHeader = {};
    BTFHeader.Magic = BTF::MAGIC;
    BTFHeader.Version = 1;
    BTFHeader.HdrLen = sizeof(BTFHeader);
    BTFHeader.StrOff = 0;
    BTFHeader.StrLen = Strings.size();
    BTFHeader.TypeOff = Strings.size();
    BTFHeader.TypeLen = Types.size();

    std::string BTFSec;
    append(BTFSec, BTFHeader);
    BTFSec.append(Strings);
    BTFSec.append(Types);

    BTF::ExtHeader ExtHeader = {};
    ExtHeader.Magic = BTF::MAGIC;
    ExtHeader.Version = 1;
    ExtHeader.HdrLen = sizeof(ExtHeader);
    ExtHeader.FieldRelocOff = 0;
    ExtHeader.FieldRelocLen = Relocs.size() + sizeof(uint32_t);
    ExtHeader.LineInfoOff = ExtHeader.FieldRelocLen;
    ExtHeader.LineInfoLen = Lines.size() + sizeof(uint32_t);

    std::string ExtSec;
    append(ExtSec, ExtHeader);
    append(ExtSec, (uint32_t)sizeof(BTF::BPFFieldReloc));
    ExtSec.append(Relocs);
    append(ExtSec, (uint32_t)sizeof(BTF::BPFLineInfo));
    ExtSec.append(Lines);

    std::string YamlBuffer;
    raw_string_ostream Yaml(YamlBuffer);
    Yaml << R"(
!ELF
FileHeader:
  Class:    ELFCLASS64)";
    if (sys::IsBigEndianHost)
      Yaml << "\n  Data:     ELFDATA2MSB";
    else
      Yaml << "\n  Data:     ELFDATA2LSB";
    Yaml << R"(
  Type:     ET_REL
  Machine:  EM_BPF
Sections:
  - Name:     foo
    Type:     SHT_PROGBITS
    Size:     0x80
  - Name:     bar
    Type:     SHT_PROGBITS
    Size:     0x80
  - Name:     .BTF
    Type:     SHT_PROGBITS
    Content: )"
         << makeBinRef(BTFSec.data(), BTFSec.size());
    Yaml << R"(
  - Name:     .BTF.ext
    Type:     SHT_PROGBITS
    Content: )"
         << makeBinRef(ExtSec.data(), ExtSec.size());

    Obj = yaml::yaml2ObjectFile(ObjStorage, YamlBuffer,
                                [](const Twine &Err) { errs() << Err; });
    return *Obj;
  }
};

TEST(BTFParserTest, allTypeKinds) {
  MockData2 D;
  D.addType({D.addString("1"), mkInfo(BTF::BTF_KIND_INT), {4}});
  D.addTail((uint32_t)0);
  D.addType({D.addString("2"), mkInfo(BTF::BTF_KIND_PTR), {1}});
  D.addType({D.addString("3"), mkInfo(BTF::BTF_KIND_ARRAY), {0}});
  D.addTail(BTF::BTFArray({1, 1, 2}));
  D.addType({D.addString("4"), mkInfo(BTF::BTF_KIND_STRUCT) | 2, {8}});
  D.addTail(BTF::BTFMember({D.addString("a"), 1, 0}));
  D.addTail(BTF::BTFMember({D.addString("b"), 1, 0}));
  D.addType({D.addString("5"), mkInfo(BTF::BTF_KIND_UNION) | 3, {8}});
  D.addTail(BTF::BTFMember({D.addString("a"), 1, 0}));
  D.addTail(BTF::BTFMember({D.addString("b"), 1, 0}));
  D.addTail(BTF::BTFMember({D.addString("c"), 1, 0}));
  D.addType({D.addString("6"), mkInfo(BTF::BTF_KIND_ENUM) | 2, {4}});
  D.addTail(BTF::BTFEnum({D.addString("U"), 1}));
  D.addTail(BTF::BTFEnum({D.addString("V"), 2}));
  D.addType({D.addString("7"), mkInfo(BTF::BTF_KIND_ENUM64) | 1, {4}});
  D.addTail(BTF::BTFEnum64({D.addString("W"), 0, 1}));
  D.addType(
      {D.addString("8"), BTF::FWD_UNION_FLAG | mkInfo(BTF::BTF_KIND_FWD), {0}});
  D.addType({D.addString("9"), mkInfo(BTF::BTF_KIND_TYPEDEF), {1}});
  D.addType({D.addString("10"), mkInfo(BTF::BTF_KIND_VOLATILE), {1}});
  D.addType({D.addString("11"), mkInfo(BTF::BTF_KIND_CONST), {1}});
  D.addType({D.addString("12"), mkInfo(BTF::BTF_KIND_RESTRICT), {1}});
  D.addType({D.addString("13"), mkInfo(BTF::BTF_KIND_FUNC_PROTO) | 1, {1}});
  D.addTail(BTF::BTFParam({D.addString("P"), 2}));
  D.addType({D.addString("14"), mkInfo(BTF::BTF_KIND_FUNC), {13}});
  D.addType({D.addString("15"), mkInfo(BTF::BTF_KIND_VAR), {2}});
  D.addTail((uint32_t)0);
  D.addType({D.addString("16"), mkInfo(BTF::BTF_KIND_DATASEC) | 3, {0}});
  D.addTail(BTF::BTFDataSec({1, 0, 4}));
  D.addTail(BTF::BTFDataSec({1, 4, 4}));
  D.addTail(BTF::BTFDataSec({1, 8, 4}));
  D.addType({D.addString("17"), mkInfo(BTF::BTF_KIND_FLOAT), {4}});
  D.addType({D.addString("18"), mkInfo(BTF::BTF_KIND_DECL_TAG), {0}});
  D.addTail((uint32_t)-1);
  D.addType({D.addString("19"), mkInfo(BTF::BTF_KIND_TYPE_TAG), {0}});

  BTFParser BTF;
  Error Err = BTF.parse(D.makeObj());
  EXPECT_FALSE(Err);

  EXPECT_EQ(D.totalTypes() + 1 /* +1 for void */, BTF.typesCount());
  for (unsigned Id = 1; Id < D.totalTypes() + 1; ++Id) {
    const BTF::CommonType *Tp = BTF.findType(Id);
    ASSERT_TRUE(Tp);
    std::string IdBuf;
    raw_string_ostream IdBufStream(IdBuf);
    IdBufStream << Id;
    EXPECT_EQ(BTF.findString(Tp->NameOff), IdBuf);
  }
}

TEST(BTFParserTest, bigStruct) {
  const uint32_t N = 1000u;
  MockData2 D;
  uint32_t FStr = D.addString("f");
  D.addType({D.addString("foo"), mkInfo(BTF::BTF_KIND_INT), {4}});
  D.addTail((uint32_t)0);
  D.addType({D.addString("big"), mkInfo(BTF::BTF_KIND_STRUCT) | N, {8}});
  for (unsigned I = 0; I < N; ++I)
    D.addTail(BTF::BTFMember({FStr, 1, 0}));
  D.addType({D.addString("bar"), mkInfo(BTF::BTF_KIND_INT), {4}});
  D.addTail((uint32_t)0);

  BTFParser BTF;
  ASSERT_SUCCEEDED(BTF.parse(D.makeObj()));
  ASSERT_EQ(BTF.typesCount(), 4u /* +1 for void */);
  const BTF::CommonType *Foo = BTF.findType(1);
  const BTF::CommonType *Big = BTF.findType(2);
  const BTF::CommonType *Bar = BTF.findType(3);
  ASSERT_TRUE(Foo);
  ASSERT_TRUE(Big);
  ASSERT_TRUE(Bar);
  EXPECT_EQ(BTF.findString(Foo->NameOff), "foo");
  EXPECT_EQ(BTF.findString(Big->NameOff), "big");
  EXPECT_EQ(BTF.findString(Bar->NameOff), "bar");
  EXPECT_EQ(Big->getVlen(), N);
}

TEST(BTFParserTest, incompleteTypes) {
  MockData2 D;
  auto IncompleteType = [&](const BTF::CommonType &Tp) {
    D.resetTypes();
    D.addType(Tp);
    EXPECT_PARSE_ERROR(D, "incomplete type definition in .BTF section");
  };

  // All kinds that need tail.
  IncompleteType({D.addString("a"), mkInfo(BTF::BTF_KIND_INT), {4}});
  IncompleteType({D.addString("b"), mkInfo(BTF::BTF_KIND_ARRAY), {0}});
  IncompleteType({D.addString("c"), mkInfo(BTF::BTF_KIND_VAR), {0}});
  IncompleteType({D.addString("d"), mkInfo(BTF::BTF_KIND_DECL_TAG), {0}});

  // All kinds with vlen.
  IncompleteType({D.addString("a"), mkInfo(BTF::BTF_KIND_STRUCT) | 2, {8}});
  IncompleteType({D.addString("b"), mkInfo(BTF::BTF_KIND_UNION) | 3, {8}});
  IncompleteType({D.addString("c"), mkInfo(BTF::BTF_KIND_ENUM) | 2, {4}});
  IncompleteType({D.addString("d"), mkInfo(BTF::BTF_KIND_ENUM64) | 1, {4}});
  IncompleteType({D.addString("e"), mkInfo(BTF::BTF_KIND_FUNC_PROTO) | 1, {1}});
  IncompleteType({D.addString("f"), mkInfo(BTF::BTF_KIND_DATASEC) | 3, {0}});

  // An unexpected tail.
  D.resetTypes();
  D.addTail((uint32_t)0);
  EXPECT_PARSE_ERROR(D, "incomplete type definition in .BTF section");
}

// Use macro to preserve line number in error message.
#define SYMBOLIZE(SecAddr, Expected)                                           \
  do {                                                                         \
    const BTF::BPFFieldReloc *R = BTF.findFieldReloc((SecAddr));               \
    ASSERT_TRUE(R);                                                            \
    SmallString<64> Symbolized;                                                \
    BTF.symbolize(R, Symbolized);                                              \
    EXPECT_EQ(Symbolized, (Expected));                                         \
  } while (false)

// Shorter name for initializers below.
using SA = SectionedAddress;

TEST(BTFParserTest, typeRelocs) {
  MockData2 D;
  uint32_t Zero = D.addString("0");
  // id 1: struct foo {}
  // id 2: union bar;
  // id 3: struct buz;
  D.addType({D.addString("foo"), mkInfo(BTF::BTF_KIND_STRUCT), {0}});
  D.addType({D.addString("bar"),
             mkInfo(BTF::BTF_KIND_FWD) | BTF::FWD_UNION_FLAG,
             {0}});
  D.addType({D.addString("buz"), mkInfo(BTF::BTF_KIND_FWD), {0}});
  D.addRelocSec({D.addString("foo"), 7});
  // List of all possible correct type relocations for type id #1.
  D.addReloc({0, 1, Zero, BTF::BTF_TYPE_ID_LOCAL});
  D.addReloc({8, 1, Zero, BTF::BTF_TYPE_ID_REMOTE});
  D.addReloc({16, 1, Zero, BTF::TYPE_EXISTENCE});
  D.addReloc({24, 1, Zero, BTF::TYPE_MATCH});
  D.addReloc({32, 1, Zero, BTF::TYPE_SIZE});
  // Forward declarations.
  D.addReloc({40, 2, Zero, BTF::TYPE_SIZE});
  D.addReloc({48, 3, Zero, BTF::TYPE_SIZE});
  // Incorrect type relocation: bad type id.
  D.addReloc({56, 42, Zero, BTF::TYPE_SIZE});
  // Incorrect type relocation: spec should be '0'.
  D.addReloc({64, 1, D.addString("10"), BTF::TYPE_SIZE});

  BTFParser BTF;
  Error E = BTF.parse(D.makeObj());
  EXPECT_FALSE(E);

  SYMBOLIZE(SA({0, 1}), "<local_type_id> [1] struct foo");
  SYMBOLIZE(SA({8, 1}), "<target_type_id> [1] struct foo");
  SYMBOLIZE(SA({16, 1}), "<type_exists> [1] struct foo");
  SYMBOLIZE(SA({24, 1}), "<type_matches> [1] struct foo");
  SYMBOLIZE(SA({32, 1}), "<type_size> [1] struct foo");
  SYMBOLIZE(SA({40, 1}), "<type_size> [2] fwd union bar");
  SYMBOLIZE(SA({48, 1}), "<type_size> [3] fwd struct buz");
  SYMBOLIZE(SA({56, 1}), "<type_size> [42] '0' <unknown type id: 42>");
  SYMBOLIZE(SA({64, 1}),
            "<type_size> [1] '10' "
            "<unexpected type-based relocation spec: should be '0'>");
}

TEST(BTFParserTest, enumRelocs) {
  MockData2 D;
  // id 1: enum { U, V }
  D.addType({D.addString("foo"), mkInfo(BTF::BTF_KIND_ENUM) | 2, {4}});
  D.addTail(BTF::BTFEnum({D.addString("U"), 1}));
  D.addTail(BTF::BTFEnum({D.addString("V"), 2}));
  // id 2: int
  D.addType({D.addString("int"), mkInfo(BTF::BTF_KIND_INT), {4}});
  D.addTail((uint32_t)0);
  // id 3: enum: uint64_t { A, B }
  D.addType({D.addString("bar"), mkInfo(BTF::BTF_KIND_ENUM64) | 2, {8}});
  D.addTail(BTF::BTFEnum64({D.addString("A"), 1, 0}));
  D.addTail(BTF::BTFEnum64({D.addString("B"), 2, 0}));

  D.addRelocSec({D.addString("foo"), 5});
  // An ok relocation accessing value #1: U.
  D.addReloc({0, 1, D.addString("0"), BTF::ENUM_VALUE_EXISTENCE});
  // An ok relocation accessing value #2: V.
  D.addReloc({8, 1, D.addString("1"), BTF::ENUM_VALUE});
  // Incorrect relocation: too many elements in string "1:0".
  D.addReloc({16, 1, D.addString("1:0"), BTF::ENUM_VALUE});
  // Incorrect relocation: type id "2" is not an enum.
  D.addReloc({24, 2, D.addString("1"), BTF::ENUM_VALUE});
  // Incorrect relocation: value #42 does not exist for enum "foo".
  D.addReloc({32, 1, D.addString("42"), BTF::ENUM_VALUE});
  // An ok relocation accessing value #1: A.
  D.addReloc({40, 3, D.addString("0"), BTF::ENUM_VALUE_EXISTENCE});
  // An ok relocation accessing value #2: B.
  D.addReloc({48, 3, D.addString("1"), BTF::ENUM_VALUE});

  BTFParser BTF;
  Error E = BTF.parse(D.makeObj());
  EXPECT_FALSE(E);

  SYMBOLIZE(SA({0, 1}), "<enumval_exists> [1] enum foo::U = 1");
  SYMBOLIZE(SA({8, 1}), "<enumval_value> [1] enum foo::V = 2");
  SYMBOLIZE(
      SA({16, 1}),
      "<enumval_value> [1] '1:0' <unexpected enumval relocation spec size>");
  SYMBOLIZE(
      SA({24, 1}),
      "<enumval_value> [2] '1' <unexpected type kind for enum relocation: 1>");
  SYMBOLIZE(SA({32, 1}), "<enumval_value> [1] '42' <bad value index: 42>");
  SYMBOLIZE(SA({40, 1}), "<enumval_exists> [3] enum bar::A = 1");
  SYMBOLIZE(SA({48, 1}), "<enumval_value> [3] enum bar::B = 2");
}

TEST(BTFParserTest, enumRelocsMods) {
  MockData2 D;
  // id 1: enum { U, V }
  D.addType({D.addString("foo"), mkInfo(BTF::BTF_KIND_ENUM) | 2, {4}});
  D.addTail(BTF::BTFEnum({D.addString("U"), 1}));
  D.addTail(BTF::BTFEnum({D.addString("V"), 2}));
  // id 2: typedef enum foo a;
  D.addType({D.addString("a"), mkInfo(BTF::BTF_KIND_TYPEDEF), {1}});
  // id 3: const enum foo;
  D.addType({D.addString(""), mkInfo(BTF::BTF_KIND_CONST), {1}});

  D.addRelocSec({D.addString("foo"), 0});
  D.addReloc({0, 2, D.addString("0"), BTF::ENUM_VALUE});
  D.addReloc({8, 3, D.addString("1"), BTF::ENUM_VALUE});

  BTFParser BTF;
  Error E = BTF.parse(D.makeObj());
  EXPECT_FALSE(E);

  SYMBOLIZE(SA({0, 1}), "<enumval_value> [2] typedef a::U = 1");
  SYMBOLIZE(SA({8, 1}), "<enumval_value> [3] const enum foo::V = 2");
}

TEST(BTFParserTest, fieldRelocs) {
  MockData2 D;
  // id 1: int
  D.addType({D.addString("int"), mkInfo(BTF::BTF_KIND_INT), {4}});
  D.addTail((uint32_t)0);
  // id 2: struct foo { int a; int b; }
  D.addType({D.addString("foo"), mkInfo(BTF::BTF_KIND_STRUCT) | 2, {8}});
  D.addTail(BTF::BTFMember({D.addString("a"), 1, 0}));
  D.addTail(BTF::BTFMember({D.addString("b"), 1, 0}));
  // id 3: array of struct foo.
  D.addType({D.addString(""), mkInfo(BTF::BTF_KIND_ARRAY), {0}});
  D.addTail(BTF::BTFArray({2, 1, 2}));
  // id 4: struct bar { struct foo u[2]; int v; }
  D.addType({D.addString("bar"), mkInfo(BTF::BTF_KIND_STRUCT) | 2, {8}});
  D.addTail(BTF::BTFMember({D.addString("u"), 3, 0}));
  D.addTail(BTF::BTFMember({D.addString("v"), 1, 0}));
  // id 5: array with bad element type id.
  D.addType({D.addString(""), mkInfo(BTF::BTF_KIND_ARRAY), {0}});
  D.addTail(BTF::BTFArray({42, 1, 2}));
  // id 6: struct buz { <bad type> u[2]; <bad type> v; }
  D.addType({D.addString("bar"), mkInfo(BTF::BTF_KIND_STRUCT) | 2, {8}});
  D.addTail(BTF::BTFMember({D.addString("u"), 5, 0}));
  D.addTail(BTF::BTFMember({D.addString("v"), 42, 0}));

  D.addRelocSec({D.addString("foo"), 0 /* patched automatically */});
  // All field relocations kinds for struct bar::v.
  D.addReloc({0, 4, D.addString("0:1"), BTF::FIELD_BYTE_OFFSET});
  D.addReloc({8, 4, D.addString("0:1"), BTF::FIELD_BYTE_SIZE});
  D.addReloc({16, 4, D.addString("0:1"), BTF::FIELD_EXISTENCE});
  D.addReloc({24, 4, D.addString("0:1"), BTF::FIELD_SIGNEDNESS});
  D.addReloc({32, 4, D.addString("0:1"), BTF::FIELD_LSHIFT_U64});
  D.addReloc({40, 4, D.addString("0:1"), BTF::FIELD_RSHIFT_U64});
  // Non-zero first idx.
  D.addReloc({48, 4, D.addString("7:1"), BTF::FIELD_BYTE_OFFSET});
  // Access through array and struct: struct bar::u[1].a.
  D.addReloc({56, 4, D.addString("0:0:1:0"), BTF::FIELD_BYTE_OFFSET});
  // Access through array and struct: struct bar::u[1].b.
  D.addReloc({64, 4, D.addString("0:0:1:1"), BTF::FIELD_BYTE_OFFSET});
  // Incorrect relocation: empty access string.
  D.addReloc({72, 4, D.addString(""), BTF::FIELD_BYTE_OFFSET});
  // Incorrect relocation: member index out of range (only two members in bar).
  D.addReloc({80, 4, D.addString("0:2"), BTF::FIELD_BYTE_OFFSET});
  // Incorrect relocation: unknown element type id (buz::u[0] access).
  D.addReloc({88, 6, D.addString("0:0:0"), BTF::FIELD_BYTE_OFFSET});
  // Incorrect relocation: unknown member type id (buz::v access).
  D.addReloc({96, 6, D.addString("0:1:0"), BTF::FIELD_BYTE_OFFSET});
  // Incorrect relocation: non structural type in the middle of access string
  //   struct bar::v.<something>.
  D.addReloc({104, 4, D.addString("0:1:0"), BTF::FIELD_BYTE_OFFSET});

  BTFParser BTF;
  Error E = BTF.parse(D.makeObj());
  EXPECT_FALSE(E);

  SYMBOLIZE(SA({0, 1}), "<byte_off> [4] struct bar::v (0:1)");
  SYMBOLIZE(SA({8, 1}), "<byte_sz> [4] struct bar::v (0:1)");
  SYMBOLIZE(SA({16, 1}), "<field_exists> [4] struct bar::v (0:1)");
  SYMBOLIZE(SA({24, 1}), "<signed> [4] struct bar::v (0:1)");
  SYMBOLIZE(SA({32, 1}), "<lshift_u64> [4] struct bar::v (0:1)");
  SYMBOLIZE(SA({40, 1}), "<rshift_u64> [4] struct bar::v (0:1)");
  SYMBOLIZE(SA({48, 1}), "<byte_off> [4] struct bar::[7].v (7:1)");
  SYMBOLIZE(SA({56, 1}), "<byte_off> [4] struct bar::u[1].a (0:0:1:0)");
  SYMBOLIZE(SA({64, 1}), "<byte_off> [4] struct bar::u[1].b (0:0:1:1)");
  SYMBOLIZE(SA({72, 1}), "<byte_off> [4] '' <field spec too short>");
  SYMBOLIZE(SA({80, 1}),
            "<byte_off> [4] '0:2' "
            "<member index 2 for spec sub-string 1 is out of range>");
  SYMBOLIZE(SA({88, 1}), "<byte_off> [6] '0:0:0' "
                         "<unknown element type id 42 for spec sub-string 2>");
  SYMBOLIZE(SA({96, 1}), "<byte_off> [6] '0:1:0' "
                         "<unknown member type id 42 for spec sub-string 1>");
  SYMBOLIZE(SA({104, 1}), "<byte_off> [4] '0:1:0' "
                          "<unexpected type kind 1 for spec sub-string 2>");
}

TEST(BTFParserTest, fieldRelocsMods) {
  MockData2 D;
  // struct foo {
  //   int u;
  // }
  // typedef struct foo bar;
  // struct buz {
  //   const bar v;
  // }
  // typedef buz quux;
  // const volatile restrict quux <some-var>;
  uint32_t Int =
      D.addType({D.addString("int"), mkInfo(BTF::BTF_KIND_INT), {4}});
  D.addTail((uint32_t)0);
  uint32_t Foo =
      D.addType({D.addString("foo"), mkInfo(BTF::BTF_KIND_STRUCT) | 1, {4}});
  D.addTail(BTF::BTFMember({D.addString("u"), Int, 0}));
  uint32_t Bar =
      D.addType({D.addString("bar"), mkInfo(BTF::BTF_KIND_TYPEDEF), {Foo}});
  uint32_t CBar =
      D.addType({D.addString("bar"), mkInfo(BTF::BTF_KIND_CONST), {Bar}});
  uint32_t Buz =
      D.addType({D.addString("buz"), mkInfo(BTF::BTF_KIND_STRUCT) | 1, {4}});
  D.addTail(BTF::BTFMember({D.addString("v"), CBar, 0}));
  uint32_t Quux =
      D.addType({D.addString("quux"), mkInfo(BTF::BTF_KIND_TYPEDEF), {Buz}});
  uint32_t RQuux =
      D.addType({D.addString(""), mkInfo(BTF::BTF_KIND_RESTRICT), {Quux}});
  uint32_t VRQuux =
      D.addType({D.addString(""), mkInfo(BTF::BTF_KIND_VOLATILE), {RQuux}});
  uint32_t CVRQuux =
      D.addType({D.addString(""), mkInfo(BTF::BTF_KIND_CONST), {VRQuux}});
  uint32_t CUnknown =
      D.addType({D.addString(""), mkInfo(BTF::BTF_KIND_CONST), {77}});
  uint32_t CVUnknown =
      D.addType({D.addString(""), mkInfo(BTF::BTF_KIND_VOLATILE), {CUnknown}});

  D.addRelocSec({D.addString("foo"), 0});
  D.addReloc({0, Bar, D.addString("0:0"), BTF::FIELD_BYTE_OFFSET});
  D.addReloc({8, CVRQuux, D.addString("0:0:0"), BTF::FIELD_BYTE_OFFSET});
  D.addReloc({16, CVUnknown, D.addString("0:1:2"), BTF::FIELD_BYTE_OFFSET});

  BTFParser BTF;
  Error E = BTF.parse(D.makeObj());
  EXPECT_FALSE(E);

  // Should show modifiers / name of typedef.
  SYMBOLIZE(SA({0, 1}), "<byte_off> [3] typedef bar::u (0:0)");
  SYMBOLIZE(SA({8, 1}),
            "<byte_off> [9] const volatile restrict typedef quux::v.u (0:0:0)");
  SYMBOLIZE(SA({16, 1}),
            "<byte_off> [11] '0:1:2' <unknown type id: 77 in modifiers chain>");
}

TEST(BTFParserTest, relocTypeTagAndVoid) {
  MockData2 D;
  // __attribute__((type_tag("tag"))) void
  uint32_t Tag =
      D.addType({D.addString("tag"), mkInfo(BTF::BTF_KIND_TYPE_TAG), {0}});

  D.addRelocSec({D.addString("foo"), 0});
  D.addReloc({0, Tag, D.addString("0"), BTF::TYPE_EXISTENCE});
  D.addReloc({8, 0 /* void */, D.addString("0"), BTF::TYPE_EXISTENCE});

  BTFParser BTF;
  Error E = BTF.parse(D.makeObj());
  EXPECT_FALSE(E);

  SYMBOLIZE(SA({0, 1}), "<type_exists> [1] type_tag(\"tag\") void");
  SYMBOLIZE(SA({8, 1}), "<type_exists> [0] void");
}

TEST(BTFParserTest, longRelocModifiersCycle) {
  MockData2 D;

  D.addType(
      {D.addString(""), mkInfo(BTF::BTF_KIND_CONST), {1 /* ourselves */}});
  D.addRelocSec({D.addString("foo"), 0});
  D.addReloc({0, 1, D.addString(""), BTF::TYPE_EXISTENCE});

  BTFParser BTF;
  Error E = BTF.parse(D.makeObj());
  EXPECT_FALSE(E);

  SYMBOLIZE(SA({0, 1}), "<type_exists> [1] '' <modifiers chain is too long>");
}

TEST(BTFParserTest, relocAnonFieldsAndTypes) {
  MockData2 D;

  // struct {
  //   int :32;
  // } v;
  uint32_t Int =
      D.addType({D.addString("int"), mkInfo(BTF::BTF_KIND_INT), {4}});
  D.addTail((uint32_t)0);
  uint32_t Anon =
      D.addType({D.addString(""), mkInfo(BTF::BTF_KIND_STRUCT) | 1, {4}});
  D.addTail(BTF::BTFMember({D.addString(""), Int, 0}));

  D.addRelocSec({D.addString("foo"), 0});
  D.addReloc({0, Anon, D.addString("0"), BTF::TYPE_EXISTENCE});
  D.addReloc({8, Anon, D.addString("0:0"), BTF::FIELD_BYTE_OFFSET});

  BTFParser BTF;
  Error E = BTF.parse(D.makeObj());
  EXPECT_FALSE(E);

  SYMBOLIZE(SA({0, 1}), "<type_exists> [2] struct <anon 2>");
  SYMBOLIZE(SA({8, 1}), "<byte_off> [2] struct <anon 2>::<anon 0> (0:0)");
}

TEST(BTFParserTest, miscBadRelos) {
  MockData2 D;

  uint32_t S = D.addType({D.addString("S"), mkInfo(BTF::BTF_KIND_STRUCT), {0}});

  D.addRelocSec({D.addString("foo"), 0});
  D.addReloc({0, 0, D.addString(""), 777});
  D.addReloc({8, S, D.addString("abc"), BTF::FIELD_BYTE_OFFSET});
  D.addReloc({16, S, D.addString("0#"), BTF::FIELD_BYTE_OFFSET});

  BTFParser BTF;
  Error E = BTF.parse(D.makeObj());
  EXPECT_FALSE(E);

  SYMBOLIZE(SA({0, 1}),
            "<reloc kind #777> [0] '' <unknown relocation kind: 777>");
  SYMBOLIZE(SA({8, 1}), "<byte_off> [1] 'abc' <spec string is not a number>");
  SYMBOLIZE(SA({16, 1}),
            "<byte_off> [1] '0#' <unexpected spec string delimiter: '#'>");
}

TEST(BTFParserTest, relocsMultipleSections) {
  MockData2 D;

  uint32_t S = D.addType({D.addString("S"), mkInfo(BTF::BTF_KIND_STRUCT), {0}});
  uint32_t T = D.addType({D.addString("T"), mkInfo(BTF::BTF_KIND_STRUCT), {0}});

  D.addRelocSec({D.addString("foo"), 0});
  D.addReloc({0, S, D.addString(""), BTF::TYPE_EXISTENCE});
  D.addReloc({8, S, D.addString(""), BTF::TYPE_EXISTENCE});

  D.addRelocSec({D.addString("bar"), 0});
  D.addReloc({8, T, D.addString(""), BTF::TYPE_EXISTENCE});
  D.addReloc({16, T, D.addString(""), BTF::TYPE_EXISTENCE});

  BTFParser BTF;
  Error E = BTF.parse(D.makeObj());
  EXPECT_FALSE(E);

  EXPECT_TRUE(BTF.findFieldReloc({0, 1}));
  EXPECT_TRUE(BTF.findFieldReloc({8, 1}));
  EXPECT_FALSE(BTF.findFieldReloc({16, 1}));

  EXPECT_FALSE(BTF.findFieldReloc({0, 2}));
  EXPECT_TRUE(BTF.findFieldReloc({8, 2}));
  EXPECT_TRUE(BTF.findFieldReloc({16, 2}));

  EXPECT_FALSE(BTF.findFieldReloc({0, 3}));
  EXPECT_FALSE(BTF.findFieldReloc({8, 3}));
  EXPECT_FALSE(BTF.findFieldReloc({16, 3}));

  auto AssertReloType = [&](const SectionedAddress &A, const char *Name) {
    const BTF::BPFFieldReloc *Relo = BTF.findFieldReloc(A);
    ASSERT_TRUE(Relo);
    const BTF::CommonType *Type = BTF.findType(Relo->TypeID);
    ASSERT_TRUE(Type);
    EXPECT_EQ(BTF.findString(Type->NameOff), Name);
  };

  AssertReloType({8, 1}, "S");
  AssertReloType({8, 2}, "T");
}

TEST(BTFParserTest, parserResetReloAndTypes) {
  BTFParser BTF;
  MockData2 D;

  // First time: two types, two relocations.
  D.addType({D.addString("foo"), mkInfo(BTF::BTF_KIND_STRUCT), {0}});
  D.addType({D.addString("bar"), mkInfo(BTF::BTF_KIND_STRUCT), {0}});
  D.addRelocSec({D.addString("foo"), 0});
  D.addReloc({0, 1, D.addString(""), BTF::TYPE_EXISTENCE});
  D.addReloc({8, 2, D.addString(""), BTF::TYPE_EXISTENCE});

  Error E1 = BTF.parse(D.makeObj());
  EXPECT_FALSE(E1);

  ASSERT_TRUE(BTF.findType(1));
  EXPECT_EQ(BTF.findString(BTF.findType(1)->NameOff), "foo");
  EXPECT_TRUE(BTF.findType(2));
  EXPECT_TRUE(BTF.findFieldReloc({0, 1}));
  EXPECT_TRUE(BTF.findFieldReloc({8, 1}));

  // Second time: one type, one relocation.
  D.reset();
  D.addType({D.addString("buz"), mkInfo(BTF::BTF_KIND_STRUCT), {0}});
  D.addRelocSec({D.addString("foo"), 0});
  D.addReloc({0, 1, D.addString(""), BTF::TYPE_EXISTENCE});

  Error E2 = BTF.parse(D.makeObj());
  EXPECT_FALSE(E2);

  ASSERT_TRUE(BTF.findType(1));
  EXPECT_EQ(BTF.findString(BTF.findType(1)->NameOff), "buz");
  EXPECT_FALSE(BTF.findType(2));
  EXPECT_TRUE(BTF.findFieldReloc({0, 1}));
  EXPECT_FALSE(BTF.findFieldReloc({8, 1}));
}

TEST(BTFParserTest, selectiveLoad) {
  BTFParser BTF1, BTF2, BTF3;
  MockData2 D;

  D.addType({D.addString("foo"), mkInfo(BTF::BTF_KIND_STRUCT), {0}});
  D.addRelocSec({D.addString("foo"), 0});
  D.addReloc({0, 1, D.addString(""), BTF::TYPE_EXISTENCE});
  D.addLinesSec({D.addString("foo"), 0});
  D.addLine({0, D.addString("file.c"), D.addString("some line"), LC(2, 3)});

  BTFParser::ParseOptions Opts;

  ObjectFile &Obj1 = D.makeObj();
  Opts = {};
  Opts.LoadLines = true;
  ASSERT_SUCCEEDED(BTF1.parse(Obj1, Opts));

  Opts = {};
  Opts.LoadTypes = true;
  ASSERT_SUCCEEDED(BTF2.parse(Obj1, Opts));

  Opts = {};
  Opts.LoadRelocs = true;
  ASSERT_SUCCEEDED(BTF3.parse(Obj1, Opts));

  EXPECT_TRUE(BTF1.findLineInfo({0, 1}));
  EXPECT_FALSE(BTF2.findLineInfo({0, 1}));
  EXPECT_FALSE(BTF3.findLineInfo({0, 1}));

  EXPECT_FALSE(BTF1.findType(1));
  EXPECT_TRUE(BTF2.findType(1));
  EXPECT_FALSE(BTF3.findType(1));

  EXPECT_FALSE(BTF1.findFieldReloc({0, 1}));
  EXPECT_FALSE(BTF2.findFieldReloc({0, 1}));
  EXPECT_TRUE(BTF3.findFieldReloc({0, 1}));
}

} // namespace
