|
11 | 11 | #include "llvm/IR/LLVMContext.h"
|
12 | 12 | #include "llvm/IR/Module.h"
|
13 | 13 | #include "llvm/Passes/PassBuilder.h"
|
| 14 | +#include "llvm/ProfileData/InstrProfReader.h" |
| 15 | +#include "llvm/ProfileData/InstrProfWriter.h" |
14 | 16 | #include "llvm/ProfileData/MemProf.h"
|
15 | 17 | #include "llvm/Support/SourceMgr.h"
|
| 18 | +#include "llvm/Testing/Support/Error.h" |
16 | 19 | #include "llvm/Transforms/Instrumentation/MemProfiler.h"
|
17 | 20 |
|
18 | 21 | #include "gmock/gmock.h"
|
|
21 | 24 | namespace {
|
22 | 25 | using namespace llvm;
|
23 | 26 | using namespace llvm::memprof;
|
| 27 | +using testing::Contains; |
| 28 | +using testing::ElementsAre; |
24 | 29 | using testing::FieldsAre;
|
25 | 30 | using testing::Pair;
|
26 | 31 | using testing::SizeIs;
|
| 32 | +using testing::UnorderedElementsAre; |
27 | 33 |
|
28 | 34 | TEST(MemProf, ExtractDirectCallsFromIR) {
|
29 | 35 | // The following IR is generated from:
|
@@ -298,4 +304,189 @@ attributes #2 = { builtin allocsize(0) }
|
298 | 304 | ASSERT_THAT(FooCallSites, SizeIs(1));
|
299 | 305 | EXPECT_THAT(FooCallSites[0], Pair(FieldsAre(1U, 10U), 0));
|
300 | 306 | }
|
| 307 | + |
| 308 | +// Populate those fields returned by getHotColdSchema. |
| 309 | +MemInfoBlock makePartialMIB() { |
| 310 | + MemInfoBlock MIB; |
| 311 | + MIB.AllocCount = 1; |
| 312 | + MIB.TotalSize = 5; |
| 313 | + MIB.TotalLifetime = 10; |
| 314 | + MIB.TotalLifetimeAccessDensity = 23; |
| 315 | + return MIB; |
| 316 | +} |
| 317 | + |
| 318 | +IndexedMemProfRecord |
| 319 | +makeRecordV2(std::initializer_list<::llvm::memprof::CallStackId> AllocFrames, |
| 320 | + std::initializer_list<::llvm::memprof::CallStackId> CallSiteFrames, |
| 321 | + const MemInfoBlock &Block, const memprof::MemProfSchema &Schema) { |
| 322 | + llvm::memprof::IndexedMemProfRecord MR; |
| 323 | + for (const auto &CSId : AllocFrames) { |
| 324 | + // We don't populate IndexedAllocationInfo::CallStack because we use it only |
| 325 | + // in Version1. |
| 326 | + MR.AllocSites.push_back({{}, CSId, Block, Schema}); |
| 327 | + } |
| 328 | + for (const auto &CSId : CallSiteFrames) |
| 329 | + MR.CallSiteIds.push_back(CSId); |
| 330 | + return MR; |
| 331 | +} |
| 332 | + |
| 333 | +static const auto Err = [](Error E) { |
| 334 | + FAIL() << E; |
| 335 | + consumeError(std::move(E)); |
| 336 | +}; |
| 337 | + |
| 338 | +// Make sure that we can undrift direct calls. |
| 339 | +TEST(MemProf, ComputeUndriftingMap) { |
| 340 | + // Suppose that the source code has changed from: |
| 341 | + // |
| 342 | + // void bar(); |
| 343 | + // void baz(); |
| 344 | + // void zzz(); |
| 345 | + // |
| 346 | + // void foo() { |
| 347 | + // /**/ bar(); // LineLocation(1, 8) |
| 348 | + // zzz(); // LineLocation(2, 3) |
| 349 | + // baz(); // LineLocation(3, 3) |
| 350 | + // } |
| 351 | + // |
| 352 | + // to: |
| 353 | + // |
| 354 | + // void bar(); |
| 355 | + // void baz(); |
| 356 | + // |
| 357 | + // void foo() { |
| 358 | + // bar(); // LineLocation(1, 3) |
| 359 | + // /**/ baz(); // LineLocation(2, 8) |
| 360 | + // } |
| 361 | + // |
| 362 | + // Notice that the calls to bar and baz have drifted while zzz has been |
| 363 | + // removed. |
| 364 | + StringRef IR = R"IR( |
| 365 | +define dso_local void @_Z3foov() #0 !dbg !10 { |
| 366 | +entry: |
| 367 | + call void @_Z3barv(), !dbg !13 |
| 368 | + call void @_Z3bazv(), !dbg !14 |
| 369 | + ret void, !dbg !15 |
| 370 | +} |
| 371 | +
|
| 372 | +declare !dbg !16 void @_Z3barv() #1 |
| 373 | +
|
| 374 | +declare !dbg !17 void @_Z3bazv() #1 |
| 375 | +
|
| 376 | +attributes #0 = { mustprogress uwtable "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } |
| 377 | +attributes #1 = { "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } |
| 378 | +
|
| 379 | +!llvm.dbg.cu = !{!0} |
| 380 | +!llvm.module.flags = !{!2, !3, !4, !5, !6, !7, !8} |
| 381 | +!llvm.ident = !{!9} |
| 382 | +
|
| 383 | +!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang", isOptimized: true, runtimeVersion: 0, emissionKind: LineTablesOnly, splitDebugInlining: false, debugInfoForProfiling: true, nameTableKind: None) |
| 384 | +!1 = !DIFile(filename: "foobar.cc", directory: "/") |
| 385 | +!2 = !{i32 7, !"Dwarf Version", i32 5} |
| 386 | +!3 = !{i32 2, !"Debug Info Version", i32 3} |
| 387 | +!4 = !{i32 1, !"wchar_size", i32 4} |
| 388 | +!5 = !{i32 1, !"MemProfProfileFilename", !"memprof.profraw"} |
| 389 | +!6 = !{i32 8, !"PIC Level", i32 2} |
| 390 | +!7 = !{i32 7, !"PIE Level", i32 2} |
| 391 | +!8 = !{i32 7, !"uwtable", i32 2} |
| 392 | +!9 = !{!"clang"} |
| 393 | +!10 = distinct !DISubprogram(name: "foo", linkageName: "_Z3foov", scope: !1, file: !1, line: 4, type: !11, scopeLine: 4, flags: DIFlagPrototyped | DIFlagAllCallsDescribed, spFlags: DISPFlagDefinition | DISPFlagOptimized, unit: !0) |
| 394 | +!11 = !DISubroutineType(types: !12) |
| 395 | +!12 = !{} |
| 396 | +!13 = !DILocation(line: 5, column: 3, scope: !10) |
| 397 | +!14 = !DILocation(line: 6, column: 8, scope: !10) |
| 398 | +!15 = !DILocation(line: 7, column: 1, scope: !10) |
| 399 | +!16 = !DISubprogram(name: "bar", linkageName: "_Z3barv", scope: !1, file: !1, line: 1, type: !11, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized) |
| 400 | +!17 = !DISubprogram(name: "baz", linkageName: "_Z3bazv", scope: !1, file: !1, line: 2, type: !11, flags: DIFlagPrototyped, spFlags: DISPFlagOptimized) |
| 401 | +)IR"; |
| 402 | + |
| 403 | + LLVMContext Ctx; |
| 404 | + SMDiagnostic SMErr; |
| 405 | + std::unique_ptr<Module> M = parseAssemblyString(IR, SMErr, Ctx); |
| 406 | + ASSERT_TRUE(M); |
| 407 | + |
| 408 | + auto *F = M->getFunction("_Z3foov"); |
| 409 | + ASSERT_NE(F, nullptr); |
| 410 | + |
| 411 | + TargetLibraryInfoWrapperPass WrapperPass; |
| 412 | + auto &TLI = WrapperPass.getTLI(*F); |
| 413 | + auto Calls = extractCallsFromIR(*M, TLI); |
| 414 | + |
| 415 | + uint64_t GUIDFoo = IndexedMemProfRecord::getGUID("_Z3foov"); |
| 416 | + uint64_t GUIDBar = IndexedMemProfRecord::getGUID("_Z3barv"); |
| 417 | + uint64_t GUIDBaz = IndexedMemProfRecord::getGUID("_Z3bazv"); |
| 418 | + uint64_t GUIDZzz = IndexedMemProfRecord::getGUID("_Z3zzzv"); |
| 419 | + |
| 420 | + // Verify that extractCallsFromIR extracts caller-callee pairs as expected. |
| 421 | + EXPECT_THAT(Calls, |
| 422 | + UnorderedElementsAre(Pair( |
| 423 | + GUIDFoo, ElementsAre(Pair(LineLocation(1, 3), GUIDBar), |
| 424 | + Pair(LineLocation(2, 8), GUIDBaz))))); |
| 425 | + |
| 426 | + llvm::InstrProfWriter Writer; |
| 427 | + std::unique_ptr<IndexedInstrProfReader> Reader; |
| 428 | + |
| 429 | + const MemInfoBlock MIB = makePartialMIB(); |
| 430 | + |
| 431 | + Writer.setMemProfVersionRequested(memprof::Version3); |
| 432 | + Writer.setMemProfFullSchema(false); |
| 433 | + |
| 434 | + ASSERT_THAT_ERROR(Writer.mergeProfileKind(InstrProfKind::MemProf), |
| 435 | + Succeeded()); |
| 436 | + |
| 437 | + const std::pair<memprof::FrameId, memprof::Frame> Frames[] = { |
| 438 | + // The call sites within foo. |
| 439 | + {0, {GUIDFoo, 1, 8, false}}, |
| 440 | + {1, {GUIDFoo, 2, 3, false}}, |
| 441 | + {2, {GUIDFoo, 3, 3, false}}, |
| 442 | + // Line/column numbers below don't matter. |
| 443 | + {3, {GUIDBar, 9, 9, false}}, |
| 444 | + {4, {GUIDZzz, 9, 9, false}}, |
| 445 | + {5, {GUIDBaz, 9, 9, false}}}; |
| 446 | + for (const auto &[FrameId, Frame] : Frames) |
| 447 | + Writer.addMemProfFrame(FrameId, Frame, Err); |
| 448 | + |
| 449 | + const std::pair<memprof::CallStackId, SmallVector<memprof::FrameId>> |
| 450 | + CallStacks[] = { |
| 451 | + {0x111, {3, 0}}, // bar called by foo |
| 452 | + {0x222, {4, 1}}, // zzz called by foo |
| 453 | + {0x333, {5, 2}} // baz called by foo |
| 454 | + }; |
| 455 | + for (const auto &[CSId, CallStack] : CallStacks) |
| 456 | + Writer.addMemProfCallStack(CSId, CallStack, Err); |
| 457 | + |
| 458 | + const IndexedMemProfRecord IndexedMR = makeRecordV2( |
| 459 | + /*AllocFrames=*/{0x111, 0x222, 0x333}, |
| 460 | + /*CallSiteFrames=*/{}, MIB, memprof::getHotColdSchema()); |
| 461 | + Writer.addMemProfRecord(/*Id=*/0x9999, IndexedMR); |
| 462 | + |
| 463 | + auto Profile = Writer.writeBuffer(); |
| 464 | + |
| 465 | + auto ReaderOrErr = |
| 466 | + IndexedInstrProfReader::create(std::move(Profile), nullptr); |
| 467 | + EXPECT_THAT_ERROR(ReaderOrErr.takeError(), Succeeded()); |
| 468 | + Reader = std::move(ReaderOrErr.get()); |
| 469 | + |
| 470 | + // Verify that getMemProfCallerCalleePairs extracts caller-callee pairs as |
| 471 | + // expected. |
| 472 | + auto Pairs = Reader->getMemProfCallerCalleePairs(); |
| 473 | + ASSERT_THAT(Pairs, SizeIs(4)); |
| 474 | + ASSERT_THAT( |
| 475 | + Pairs, |
| 476 | + Contains(Pair(GUIDFoo, ElementsAre(Pair(LineLocation(1, 8), GUIDBar), |
| 477 | + Pair(LineLocation(2, 3), GUIDZzz), |
| 478 | + Pair(LineLocation(3, 3), GUIDBaz))))); |
| 479 | + |
| 480 | + // Verify that computeUndriftMap identifies undrifting opportunities: |
| 481 | + // |
| 482 | + // Profile IR |
| 483 | + // (Line: 1, Column: 8) -> (Line: 1, Column: 3) |
| 484 | + // (Line: 3, Column: 3) -> (Line: 2, Column: 8) |
| 485 | + auto UndriftMap = computeUndriftMap(*M, Reader.get(), TLI); |
| 486 | + ASSERT_THAT(UndriftMap, |
| 487 | + UnorderedElementsAre(Pair( |
| 488 | + GUIDFoo, UnorderedElementsAre( |
| 489 | + Pair(LineLocation(1, 8), LineLocation(1, 3)), |
| 490 | + Pair(LineLocation(3, 3), LineLocation(2, 8)))))); |
| 491 | +} |
301 | 492 | } // namespace
|
0 commit comments