llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang-static-analyzer-1 Author: Balázs Benics (steakhal) <details> <summary>Changes</summary> rdar://170258016 --- Full diff: https://github.com/llvm/llvm-project/pull/189681.diff 10 Files Affected: - (modified) clang/include/clang/ScalableStaticAnalysisFramework/Analyses/CallGraph/CallGraphSummary.h (+2-1) - (modified) clang/include/clang/ScalableStaticAnalysisFramework/SSAFBuiltinForceLinker.h (+5) - (modified) clang/lib/ScalableStaticAnalysisFramework/Analyses/CMakeLists.txt (+1) - (modified) clang/lib/ScalableStaticAnalysisFramework/Analyses/CallGraph/CallGraphExtractor.cpp (+2-1) - (added) clang/lib/ScalableStaticAnalysisFramework/Analyses/CallGraph/CallGraphJSONFormat.cpp (+142) - (modified) clang/lib/ScalableStaticAnalysisFramework/Frontend/TUSummaryExtractorFrontendAction.cpp (+4) - (added) clang/test/Analysis/Scalable/call-graph.cpp (+20) - (modified) clang/test/Analysis/Scalable/ssaf-format/list.test (+4-3) - (modified) clang/unittests/ScalableStaticAnalysisFramework/Analyses/CallGraph/CallGraphExtractorTest.cpp (+5-3) - (modified) llvm/utils/gn/secondary/clang/lib/ScalableStaticAnalysisFramework/Analyses/BUILD.gn (+1) ``````````diff diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/CallGraph/CallGraphSummary.h b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/CallGraph/CallGraphSummary.h index ad70218d01614..8056b1001a216 100644 --- a/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/CallGraph/CallGraphSummary.h +++ b/clang/include/clang/ScalableStaticAnalysisFramework/Analyses/CallGraph/CallGraphSummary.h @@ -30,8 +30,9 @@ struct CallGraphSummary final : public EntitySummary { unsigned Column; }; + static constexpr llvm::StringLiteral Name = "CallGraph"; SummaryName getSummaryName() const override { - return SummaryName("CallGraph"); + return SummaryName(Name.str()); } /// Represents the location of the function. diff --git a/clang/include/clang/ScalableStaticAnalysisFramework/SSAFBuiltinForceLinker.h b/clang/include/clang/ScalableStaticAnalysisFramework/SSAFBuiltinForceLinker.h index 707573ce34e46..1dc50c9c58dd8 100644 --- a/clang/include/clang/ScalableStaticAnalysisFramework/SSAFBuiltinForceLinker.h +++ b/clang/include/clang/ScalableStaticAnalysisFramework/SSAFBuiltinForceLinker.h @@ -37,4 +37,9 @@ extern volatile int CallGraphExtractorAnchorSource; [[maybe_unused]] static int CallGraphExtractorAnchorDestination = CallGraphExtractorAnchorSource; +// This anchor is used to force the linker to link the CallGraph JSON format. +extern volatile int CallGraphJSONFormatAnchorSource; +[[maybe_unused]] static int CallGraphJSONFormatAnchorDestination = + CallGraphJSONFormatAnchorSource; + #endif // LLVM_CLANG_SCALABLESTATICANALYSISFRAMEWORK_SSAFBUILTINFORCELINKER_H diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/CMakeLists.txt b/clang/lib/ScalableStaticAnalysisFramework/Analyses/CMakeLists.txt index 2dcce40f886dd..df8079a7d375d 100644 --- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/CMakeLists.txt +++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/CMakeLists.txt @@ -4,6 +4,7 @@ set(LLVM_LINK_COMPONENTS add_clang_library(clangScalableStaticAnalysisFrameworkAnalyses CallGraph/CallGraphExtractor.cpp + CallGraph/CallGraphJSONFormat.cpp UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp LINK_LIBS diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/CallGraph/CallGraphExtractor.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/CallGraph/CallGraphExtractor.cpp index b2cd2e40f33b5..1dbed7e0b0d8a 100644 --- a/clang/lib/ScalableStaticAnalysisFramework/Analyses/CallGraph/CallGraphExtractor.cpp +++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/CallGraph/CallGraphExtractor.cpp @@ -98,7 +98,8 @@ void CallGraphExtractor::handleCallGraphNode(const ASTContext &Ctx, } static TUSummaryExtractorRegistry::Add<CallGraphExtractor> - RegisterExtractor("CallGraph", "Extracts static call-graph information"); + RegisterExtractor(CallGraphSummary::Name, + "Extracts static call-graph information"); // This anchor is used to force the linker to link in the generated object file // and thus register the CallGraphExtractor. diff --git a/clang/lib/ScalableStaticAnalysisFramework/Analyses/CallGraph/CallGraphJSONFormat.cpp b/clang/lib/ScalableStaticAnalysisFramework/Analyses/CallGraph/CallGraphJSONFormat.cpp new file mode 100644 index 0000000000000..33da0ab0bd205 --- /dev/null +++ b/clang/lib/ScalableStaticAnalysisFramework/Analyses/CallGraph/CallGraphJSONFormat.cpp @@ -0,0 +1,142 @@ +//===- CallGraphJSONFormat.cpp --------------------------------------------===// +// +// 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 "clang/ScalableStaticAnalysisFramework/Analyses/CallGraph/CallGraphSummary.h" +#include "clang/ScalableStaticAnalysisFramework/Core/Model/EntityId.h" +#include "clang/ScalableStaticAnalysisFramework/Core/Serialization/JSONFormat.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/Support/JSON.h" +#include <memory> + +using namespace llvm; +using namespace clang; +using namespace ssaf; + +static json::Object serialize(const EntitySummary &Summary, + JSONFormat::EntityIdToJSONFn ToJSON) { + const auto &S = static_cast<const CallGraphSummary &>(Summary); + + json::Array DirectCalleesArray; + DirectCalleesArray.reserve(S.DirectCallees.size()); + append_range(DirectCalleesArray, map_range(S.DirectCallees, ToJSON)); + + json::Array VirtualCalleesArray; + VirtualCalleesArray.reserve(S.VirtualCallees.size()); + append_range(VirtualCalleesArray, map_range(S.VirtualCallees, ToJSON)); + + return json::Object{ + {"pretty_name", json::Value(S.PrettyName)}, + {"direct_callees", std::move(DirectCalleesArray)}, + {"virtual_callees", std::move(VirtualCalleesArray)}, + {"def", + json::Object{ + {"file", json::Value(S.Definition.File)}, + {"line", json::Value(S.Definition.Line)}, + {"col", json::Value(S.Definition.Column)}, + }}, + }; +} + +static Expected<std::unique_ptr<EntitySummary>> +deserialize(const json::Object &Obj, EntityIdTable &IdTable, + JSONFormat::EntityIdFromJSONFn FromJSON) { + auto Result = std::make_unique<CallGraphSummary>(); + + auto PrettyName = Obj.getString("pretty_name"); + if (!PrettyName) { + return createStringError(inconvertibleErrorCode(), + "missing or invalid field 'pretty_name'"); + } + Result->PrettyName = PrettyName->str(); + + const json::Array *CalleesArray = Obj.getArray("direct_callees"); + if (!CalleesArray) { + return createStringError(inconvertibleErrorCode(), + "missing or invalid field 'direct_callees'"); + } + for (const auto &[Index, Value] : llvm::enumerate(*CalleesArray)) { + const json::Object *CalleeObj = Value.getAsObject(); + if (!CalleeObj) { + return createStringError( + inconvertibleErrorCode(), + "direct_callees element at index %zu is not a JSON object", Index); + } + auto ExpectedId = FromJSON(*CalleeObj); + if (!ExpectedId) { + return createStringError( + inconvertibleErrorCode(), + "invalid entity id in direct_callees at index %zu: %s", Index, + toString(ExpectedId.takeError()).c_str()); + } + Result->DirectCallees.insert(*ExpectedId); + } + + const json::Array *VirtualCalleesArray = Obj.getArray("virtual_callees"); + if (!VirtualCalleesArray) { + return createStringError(inconvertibleErrorCode(), + "missing or invalid field 'virtual_callees'"); + } + for (const auto &[Index, Value] : llvm::enumerate(*VirtualCalleesArray)) { + const json::Object *CalleeObj = Value.getAsObject(); + if (!CalleeObj) { + return createStringError( + inconvertibleErrorCode(), + "virtual_callees element at index %zu is not a JSON object", Index); + } + auto ExpectedId = FromJSON(*CalleeObj); + if (!ExpectedId) { + return createStringError( + inconvertibleErrorCode(), + "invalid entity id in virtual_callees at index %zu: %s", Index, + toString(ExpectedId.takeError()).c_str()); + } + Result->VirtualCallees.insert(*ExpectedId); + } + + const json::Object *DefObj = Obj.getObject("def"); + if (!DefObj) { + return createStringError(inconvertibleErrorCode(), + "missing or invalid field 'def'"); + } + auto File = DefObj->getString("file"); + if (!File) { + return createStringError(inconvertibleErrorCode(), + "missing or invalid field 'def.file'"); + } + auto Line = DefObj->getInteger("line"); + if (!Line) { + return createStringError(inconvertibleErrorCode(), + "missing or invalid field 'def.line'"); + } + auto Col = DefObj->getInteger("col"); + if (!Col) { + return createStringError(inconvertibleErrorCode(), + "missing or invalid field 'def.col'"); + } + Result->Definition = {File->str(), static_cast<unsigned>(*Line), + static_cast<unsigned>(*Col)}; + + return std::move(Result); +} + +namespace { +struct CallGraphJSONFormatInfo final : JSONFormat::FormatInfo { + CallGraphJSONFormatInfo() + : JSONFormat::FormatInfo(SummaryName(CallGraphSummary::Name.str()), + serialize, deserialize) {} +}; +} // namespace + +static llvm::Registry<JSONFormat::FormatInfo>::Add<CallGraphJSONFormatInfo> + RegisterFormatInfo(CallGraphSummary::Name, + "JSON Format info for CallGraph summary"); + +// This anchor is used to force the linker to link in the generated object file +// and thus register the JSON format for CallGraphSummary. +// NOLINTNEXTLINE(misc-use-internal-linkage) +volatile int CallGraphJSONFormatAnchorSource = 0; diff --git a/clang/lib/ScalableStaticAnalysisFramework/Frontend/TUSummaryExtractorFrontendAction.cpp b/clang/lib/ScalableStaticAnalysisFramework/Frontend/TUSummaryExtractorFrontendAction.cpp index 9a75b20fa548b..d4bf8d4a63fa4 100644 --- a/clang/lib/ScalableStaticAnalysisFramework/Frontend/TUSummaryExtractorFrontendAction.cpp +++ b/clang/lib/ScalableStaticAnalysisFramework/Frontend/TUSummaryExtractorFrontendAction.cpp @@ -17,6 +17,7 @@ #include "clang/ScalableStaticAnalysisFramework/Core/TUSummary/TUSummaryExtractor.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringRef.h" +#include "llvm/Support/IOSandbox.h" #include "llvm/Support/Path.h" #include <memory> #include <string> @@ -150,6 +151,9 @@ void TUSummaryRunner::HandleTranslationUnit(ASTContext &Ctx) { // First, invoke the Summary Extractors. MultiplexConsumer::HandleTranslationUnit(Ctx); + // FIXME(sandboxing): Remove this by adopting `llvm::vfs::OutputBackend`. + llvm::sys::sandbox::ScopedSetting Guard = llvm::sys::sandbox::scopedDisable(); + // Then serialize the result. if (auto Err = Format->writeTUSummary(Summary, Opts.SSAFTUSummaryFile)) { Ctx.getDiagnostics().Report(diag::warn_ssaf_write_tu_summary_failed) diff --git a/clang/test/Analysis/Scalable/call-graph.cpp b/clang/test/Analysis/Scalable/call-graph.cpp new file mode 100644 index 0000000000000..b6e7ce91ee8c7 --- /dev/null +++ b/clang/test/Analysis/Scalable/call-graph.cpp @@ -0,0 +1,20 @@ +// RUN: rm -rf %t.summary.json +// RUN: %clang_cc1 -fsyntax-only %s \ +// RUN: --ssaf-extract-summaries=CallGraph \ +// RUN: --ssaf-tu-summary-file=%t.summary.json + +// Check that the JSON validation passes. +// TODO: Enable the next line once the LinkageTable is populated. +// R U N: clang-ssaf-format --type=tu %t.summary.json + +// Check that the JSON has plausible content irrespective of the order of the fields. +// RUN: FileCheck %s --match-full-lines --input-file=%t.summary.json +// CHECK-DAG: "direct_callees": [ +// CHECK-DAG: "pretty_name": "example()", +// CHECK-DAG: "virtual_callees": [] +// CHECK-DAG: "summary_name": "CallGraph" + +void no_body(); +void example() { + no_body(); +} diff --git a/clang/test/Analysis/Scalable/ssaf-format/list.test b/clang/test/Analysis/Scalable/ssaf-format/list.test index a21d1543915f7..ffc1624c5872a 100644 --- a/clang/test/Analysis/Scalable/ssaf-format/list.test +++ b/clang/test/Analysis/Scalable/ssaf-format/list.test @@ -1,9 +1,10 @@ // Test clang-ssaf-format --list output without any loaded plugins. // RUN: clang-ssaf-format --list \ -// RUN: | FileCheck %s +// RUN: | FileCheck %s --match-full-lines // CHECK: Registered serialization formats: // CHECK-EMPTY: -// CHECK-NEXT: 1. json - JSON serialization format -// CHECK-NEXT: Analyses: (none) +// CHECK-DAG: [[NthFormat:[0-9]+]]. json - JSON serialization format +// CHECK-DAG: Analyses: +// CHECK-DAG: [[NthFormat]].[[MthSummary:[0-9]+]]. CallGraph - JSON Format info for CallGraph summary diff --git a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/CallGraph/CallGraphExtractorTest.cpp b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/CallGraph/CallGraphExtractorTest.cpp index 9e0b9e6e256a4..2557c7a62479e 100644 --- a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/CallGraph/CallGraphExtractorTest.cpp +++ b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/CallGraph/CallGraphExtractorTest.cpp @@ -120,6 +120,8 @@ template <typename... Matchers> auto hasSummaryThat(const Matchers &...Ms) { // Test fixture // ============================================================================ +static const SummaryName CallGraphName{CallGraphSummary::Name.str()}; + struct CallGraphExtractorTest : ssaf::TestFixture { TUSummary Summary = BuildNamespace(BuildNamespaceKind::CompilationUnit, "Mock.cpp"); @@ -129,7 +131,7 @@ struct CallGraphExtractorTest : ssaf::TestFixture { /// This will update the \c AST \c Builder and \c Summary data members. void runExtractor(StringRef Code, ArrayRef<std::string> Args = {}) { AST = tooling::buildASTFromCodeWithArgs(Code, Args); - auto Consumer = makeTUSummaryExtractor("CallGraph", Builder); + auto Consumer = makeTUSummaryExtractor(CallGraphName.str(), Builder); Consumer->HandleTranslationUnit(AST->getASTContext()); } @@ -207,7 +209,7 @@ CallGraphExtractorTest::findSummary(llvm::StringRef FnName) const { } EntityId ID = It->second; auto &Data = getData(Summary); - auto SummaryIt = Data.find(SummaryName("CallGraph")); + auto SummaryIt = Data.find(CallGraphName); if (SummaryIt == Data.end()) return llvm::createStringError("There is no 'CallGraph' summary"); auto EntityIt = SummaryIt->second.find(ID); @@ -343,7 +345,7 @@ TEST_F(CallGraphExtractorTest, DeclarationsOnlyNoSummary) { )cpp"); // No summary for functions without definitions. - EXPECT_FALSE(llvm::is_contained(getData(Summary), SummaryName("CallGraph"))); + EXPECT_FALSE(llvm::is_contained(getData(Summary), CallGraphName)); } TEST_F(CallGraphExtractorTest, DuplicateCallees) { diff --git a/llvm/utils/gn/secondary/clang/lib/ScalableStaticAnalysisFramework/Analyses/BUILD.gn b/llvm/utils/gn/secondary/clang/lib/ScalableStaticAnalysisFramework/Analyses/BUILD.gn index ac62574ad8534..1450d8866b71f 100644 --- a/llvm/utils/gn/secondary/clang/lib/ScalableStaticAnalysisFramework/Analyses/BUILD.gn +++ b/llvm/utils/gn/secondary/clang/lib/ScalableStaticAnalysisFramework/Analyses/BUILD.gn @@ -10,6 +10,7 @@ static_library("Analyses") { ] sources = [ "CallGraph/CallGraphExtractor.cpp", + "CallGraph/CallGraphJSONFormat.cpp", "UnsafeBufferUsage/UnsafeBufferUsageExtractor.cpp", ] } `````````` </details> https://github.com/llvm/llvm-project/pull/189681 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
