Skip to content

Conversation

@Prabhuk
Copy link
Contributor

@Prabhuk Prabhuk commented Sep 8, 2025

Introduce a new flag --call-graph-info which outputs callgraph ELF
section information to the console as a text output or as JSON output.

@github-actions
Copy link

github-actions bot commented Sep 8, 2025

✅ With the latest revision this PR passed the C/C++ code formatter.

@Prabhuk Prabhuk force-pushed the readelf_callgraph_info branch from 3499c2d to 0b9c46d Compare September 18, 2025 03:09
@Prabhuk Prabhuk requested review from frobtech and ilovepi September 19, 2025 00:12
@Prabhuk Prabhuk requested a review from jh7370 October 23, 2025 18:05
Copy link
Collaborator

@jh7370 jh7370 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please make sure to update the llvm-readobj and llvm-readelf CommandGuide documents under llvm/docs.

I'm keen to review this, as I've seen several things when skimming I'd like addressed, but don't have time to review it properly this week, due to other reviews and some planned time off.

#include "llvm/Support/DataExtractor.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/Endian.h"
#include "llvm/Support/Error.h"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm surprised some/most/all of these new headers need to be added? Is your IDE adding additional headers unnecessarily?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the IDE is adding the corresponding header whenever I initialize a type. Cleaned up the header includes. PTAL.

}
}

template <class ELFT> void GNUELFDumper<ELFT>::printCallGraphInfo() {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need a GNU-style dumper? In other words, does GNU readelf support the section? If it doesn't, this function shouldn't be implemented, or at the very most, should do the same as the LLVM dumper. See various other modes for examples.

For context: llvm-readobj has three ELF output modes: JSON (which produces legal JSON output), LLVM (which is a human-readable dictionary-like structure), and GNU. GNU output is supposed to mirror GNU's readelf output, as close as possible. This is because people expect it to be a drop-in replacement in this mode. If there isn't a GNU readelf option to dump it, we shouldn't try to make up one.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dropped the GNU dumper implementation as suggested.

Copy link
Contributor

@ilovepi ilovepi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is basically LGTM from me. Lets make sure we wait until @jh7370 weighs in w/ a full review to land though.

@Prabhuk
Copy link
Contributor Author

Prabhuk commented Nov 4, 2025

I think this is basically LGTM from me. Lets make sure we wait until @jh7370 weighs in w/ a full review to land though.

Thank you. I will be adding a few more tests and will wait for @jh7370's review.

# JSON-NEXT: "NumDirectCallees": 1,
# JSON-NEXT: "DirectCallees": [
# JSON-NEXT: {
# JSON-NEXT: "Offset": 19
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is Offset here? is it the address? offset from the start of the section?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Offset is pointless actually. It's the offset of the symbol for which we couldn't find corresponding relocation. I removed "offset" and turned it into a warning for this scenario.

Copy link
Collaborator

@jh7370 jh7370 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the delay in looking. The CommandGuide docs still haven't been updated. Please update them with the new option.

I've made another pass, but have run out of time to look at everything. Hopefully there's enough here to keep going on.

@@ -0,0 +1,150 @@
## Tests --call-graph-info prints information from call graph section.

# REQUIRES: x86-registered-target
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we convert the test input to use yaml2obj, instead of requiring x86? It looks like the file format is trivial enough that something like a YAML ELF input using ContentArray would be just as descriptive, whilst also allowing more precise control of everything.

# RUN: llvm-readelf --elf-output-style=LLVM --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t --check-prefix=LLVM
# RUN: llvm-readelf --elf-output-style=JSON --pretty-print --call-graph-info %t 2>&1 | FileCheck %s -DFILE=%t --check-prefix=JSON


Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: why the double blank line?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.



## We do not support GNU format console output for --call-graph-info as it is an LLVM only info.
# CHECK-NOT: .
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't going to do what you think it will. This checks simply that the character "." doesn't appear anywhere in the output. What you probably wanted was {{.}} which is a regex pattern of any character. However, if the output is truly empty, the better method is to use count 0. You should find other examples elsewhere in the testsuite.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Neat. Thank you. Done.

def arch_specific : FF<"arch-specific", "Display architecture-specific information">;
def bb_addr_map : FF<"bb-addr-map", "Display the BB address map section">;
def pretty_pgo_analysis_map : FF<"pretty-pgo-analysis-map", "Display PGO analysis values with formatting rather than raw numbers">;
def call_graph_info : FF<"call-graph-info", "Display call graph information">;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The option for the call graph profile abbreviates it to cg-profile. Should this also be cg-info? I don't know much about the functionality, so I might be mistaken.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These two things are unrelated. I'd like to keep the name to call_graph_info which is consistent with the flags in Clang/LLVM. Same for other similar suggestions made towards abbreviating call_graph to cg.

virtual void printGroupSections() {}
virtual void printHashHistograms() {}
virtual void printCGProfile() {}
virtual void printCallGraphInfo() {}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps printCGInfo() to mirror printCGProfile?

}
}
W.printNumber("NumIndirectTargetTypeIDs", CGInfo.IndirectTypeIDs.size());
auto IndirectTypeIdsList = SmallVector<uint64_t, 4>(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
auto IndirectTypeIdsList = SmallVector<uint64_t, 4>(
SmallVector<uint64_t, 4> IndirectTypeIdsList(

This is more standard syntax in LLVM, I feel?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Comment on lines 5330 to 5331
reportError(createError("No .llvm.callgraph section found."),
"Missing section");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://llvm.org/docs/CodingStandards.html#error-and-warning-messages

Same applies to other error/warning messages.

That being said, we don't tend to emit an error for other options that do similar things, if the respective section doesn't exist.

When a diagnostic message is appropriate, we usually prefer warnings, rather than errors, so that we can continue dumping other requested items, or we emit a warning then try to dump the information we can for the requested section and use placeholder values (e.g. "<?>").

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. PTAL.


Expected<ArrayRef<uint8_t>> SectionBytesOrErr =
Obj.getSectionContents(*CGSection);
if (!SectionBytesOrErr) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I have addressed all the cases. PTAL.

.str());
};

DataExtractor Data(SectionBytesOrErr.get(), Obj.isLE(),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you looked into using the DataExtractor::Cursor class? I think that would simplify some of your parsing logic below.

if (Name == CallGraphSectionName)
return Sec;
} else
consumeError(NameOrErr.takeError());
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

consumeError is generally a code smell.

Can an actual error be emitted here? If so, it should either be propagated or reported as a warning. If it can't ever fail here, cantFail is more appropriate.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function was not used and should've been removed in the first place. Thank you.

@github-actions
Copy link

🐧 Linux x64 Test Results

  • 186436 tests passed
  • 4868 tests skipped

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants