diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-11-18 16:35:47 +0100 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-11-18 15:45:54 +0000 |
commit | 32f5a1c56531e4210bc4cf8d8c7825d66e081888 (patch) | |
tree | eeeec6822f4d738d8454525233fd0e2e3a659e6d /chromium/tools/clang | |
parent | 99677208ff3b216fdfec551fbe548da5520cd6fb (diff) | |
download | qtwebengine-chromium-32f5a1c56531e4210bc4cf8d8c7825d66e081888.tar.gz |
BASELINE: Update Chromium to 87.0.4280.67
Change-Id: Ib157360be8c2ffb2c73125751a89f60e049c1d54
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/tools/clang')
9 files changed, 519 insertions, 32 deletions
diff --git a/chromium/tools/clang/rewrite_raw_ptr_fields/RewriteRawPtrFields.cpp b/chromium/tools/clang/rewrite_raw_ptr_fields/RewriteRawPtrFields.cpp index 27b3fbb196e..61836c65bcb 100644 --- a/chromium/tools/clang/rewrite_raw_ptr_fields/RewriteRawPtrFields.cpp +++ b/chromium/tools/clang/rewrite_raw_ptr_fields/RewriteRawPtrFields.cpp @@ -673,6 +673,20 @@ AST_MATCHER(clang::FieldDecl, overlapsOtherDeclsWithinRecordDecl) { return has_sibling_with_overlapping_location; } +// Matches RecordDecl if +// 1) it has a FieldDecl that matches the InnerMatcher +// or +// 2) it has a FieldDecl that hasType of a RecordDecl that matches the +// InnerMatcher (this recurses to any depth). +AST_MATCHER_P(clang::RecordDecl, + hasNestedFieldDecl, + clang::ast_matchers::internal::Matcher<clang::FieldDecl>, + InnerMatcher) { + auto matcher = recordDecl(has(fieldDecl(anyOf( + InnerMatcher, hasType(recordDecl(hasNestedFieldDecl(InnerMatcher))))))); + return matcher.matches(Node, Finder, Builder); +} + // Rewrites |SomeClass* field| (matched as "affectedFieldDecl") into // |CheckedPtr<SomeClass> field| and for each file rewritten in such way adds an // |#include "base/memory/checked_ptr.h"|. @@ -1041,6 +1055,20 @@ int main(int argc, const char* argv[]) { match_finder.addMatcher(char_ptr_field_decl_matcher, &char_ptr_field_decl_writer); + // See the testcases in tests/gen-global-destructor-test.cc. + auto global_destructor_matcher = varDecl( + allOf(hasGlobalStorage(), + hasType(recordDecl(hasNestedFieldDecl(field_decl_matcher))))); + FilteredExprWriter global_destructor_writer(&output_helper, + "global-destructor"); + match_finder.addMatcher(global_destructor_matcher, &global_destructor_writer); + + // Matches fields in unions - see the testcases in tests/gen-unions-test.cc. + auto union_field_decl_matcher = fieldDecl( + allOf(field_decl_matcher, hasParent(decl(recordDecl(isUnion()))))); + FilteredExprWriter union_field_decl_writer(&output_helper, "union"); + match_finder.addMatcher(union_field_decl_matcher, &union_field_decl_writer); + // Prepare and run the tool. std::unique_ptr<clang::tooling::FrontendActionFactory> factory = clang::tooling::newFrontendActionFactory(&match_finder, &output_helper); diff --git a/chromium/tools/clang/rewrite_raw_ptr_fields/manual-fields-to-ignore.txt b/chromium/tools/clang/rewrite_raw_ptr_fields/manual-fields-to-ignore.txt index 9f18a75980d..0c5bb35d34d 100644 --- a/chromium/tools/clang/rewrite_raw_ptr_fields/manual-fields-to-ignore.txt +++ b/chromium/tools/clang/rewrite_raw_ptr_fields/manual-fields-to-ignore.txt @@ -155,8 +155,20 @@ device::BluetoothDevice::adapter_ vr::LocationBarState::vector_icon vr::OmniboxSuggestion::icon +# Populated manually - assigned to |auto*| in ranged loop on an array +# initializer literal comprising of those pointers. +DownloadItemView::discard_button_ +DownloadItemView::open_now_button_ +DownloadItemView::save_button_ +DownloadItemView::scan_button_ + # Populated manually - other compile-time reasons logging::CheckOpResult::message_ # cyclic #include +views::internal::ClassPropertySetter::property_ # passed to templated param T* + +####### +# CheckedPtr2/MTECheckedPtr-specific sections +####### # Populated manually - these pointers are assigned invalid address (with top # bits sets), which CheckedPtr is unable to handle, leading to run-time crashes. @@ -165,3 +177,84 @@ blink::(anonymous namespace)::ThreadMarker::creating_thread_ blink::ControlKey::name_ performance_manager::frame_priority::BoostingVoteAggregator::Edge::src_ performance_manager::frame_priority::BoostingVoteAggregator::Edge::dst_ + +# Populated manually - these pointers crash due to a mismatching tag. +# Under investigation. +base::internal::UnretainedWrapper::ptr_ +blink::NGPhysicalContainerFragment::buffer_ +blink::DisplayItem::client_ +cc::FrameSequenceMetrics::throughput_ukm_reporter_ +cc::(anonymous namespace)::RasterTaskImpl::tile_tracing_id_ +content::MediaPlayerId::render_frame_host +content::RenderViewImpl::webview_ +mojo::core::WatcherDispatcher::last_watch_to_block_arming_ +net::IOBuffer::data_ +safe_browsing::RemoteSafeBrowsingDatabaseManager::ClientRequest::client_ + +# Populated manually - this pointer crashes, because assigned address appears +# unallocated. +x11::ReadBuffer::fds + +####### +# End of CheckedPtr2/MTECheckedPtr-specific sections +####### + +####### +# BackupRefPtr-specific sections +####### + +# Populated manually - std::is_trivially_destructible static_assert failure +blink::HTMLElementEquivalent::tag_name_ +blink::PrerendererClient::client_ +blink::weakness_marking_test::EphemeronCallbacksCounter::count_holder_ +sql::recover::InnerPageDecoder::db_reader_ +sql::recover::LeafPageDecoder::db_reader_ +WTF::ListHashSet::head_ +WTF::ListHashSet::tail_ + +# Populated manually - exit-time destructor +gpu::GpuControlList::Conditions::devices +gpu::GpuControlList::Conditions::driver_info +gpu::GpuControlList::Conditions::gl_strings +gpu::GpuControlList::Conditions::machine_model_info +gpu::GpuControlList::Conditions::intel_gpu_series_list +gpu::GpuControlList::Conditions::more +gpu::GpuControlList::Entry::features +gpu::GpuControlList::Entry::disabled_extensions +gpu::GpuControlList::Entry::disabled_webgl_extensions +gpu::GpuControlList::Entry::cr_bugs +gpu::GpuControlList::Entry::exceptions +media::ListElementInfo::id_info_ +media::SupportedTypeInfo::codecs +tracing::(anonymous namespace)::AllowlistEntry::arg_name_filter +net::(anonymous namespace)::CTRequiredPolicy::roots +net::(anonymous namespace)::CTRequiredPolicy::exceptions +net::HttpCache::Transaction::SetRequest(const net::NetLogWithSource &)::(anonymous struct)::search +net::TransportSecurityStateSource::Pinset::accepted_pins +net::TransportSecurityStateSource::Pinset::rejected_pins +net::TransportSecurityStateSource::expect_ct_report_uris +net::TransportSecurityStateSource::pinsets +WTF::StaticSingleton::InstanceStorage::pointer_ + +# Populated manually - attempt to use a deleted function (union's destructor is +# deleted, because an inner struct has CheckedPtr field) +flags_ui::FeatureEntry::(anonymous union)::(anonymous struct)::feature +ui::NativeTheme::MenuSeparatorExtraParams::paint_rect + +# Populated manually - flexible array with non-trivial destruction +blink::ShapeResultView::(anonymous struct)::alignment + +# Populated manually - these pointers crash, because a base class ctor accesses +# child class ptr fields before they're initialized. +blink::CacheEntry::next_ +blink::CacheEntry::prev_ +WTF::LruCache::next_ +WTF::LruCache::prev_ + +# Populated manually - these pointers crash, due to use-after-move. +gpu::gles2::PassthroughProgramCache::ProgramCacheValue::program_cache_ +gpu::gles2::ProgramCache::ScopedCacheUse::cache_ + +####### +# End of BackupRefPtr-specific sections +####### diff --git a/chromium/tools/clang/rewrite_raw_ptr_fields/manual-paths-to-ignore.txt b/chromium/tools/clang/rewrite_raw_ptr_fields/manual-paths-to-ignore.txt index 62e16dc46c9..2bbcada8bbc 100644 --- a/chromium/tools/clang/rewrite_raw_ptr_fields/manual-paths-to-ignore.txt +++ b/chromium/tools/clang/rewrite_raw_ptr_fields/manual-paths-to-ignore.txt @@ -24,6 +24,22 @@ base/android/linker/ tools/ net/tools/ +# Exclude code that only runs inside a renderer process - renderer +# processes are excluded for now from the MiraclePtr project scope, +# because they are sensitive to performance regressions (to a much higher +# degree than, say, the Browser process). +# +# Note that some renderer-only directories are already excluded +# elsewhere - for example "v8/" is excluded in another part of this +# file. +# +# Also, note that isInThirdPartyLocation AST matcher in +# RewriteRawPtrFields.cpp explicitly includes third_party/blink +# (because it is in the same git repository as the rest of Chromium), +# but we go ahead and exclude it below. +/renderer/ # (e.g. //content/renderer/ or //components/visitedlink/renderer/). +third_party/blink/ + # Exclude paths in separate repositories - i.e. in directories that # 1. Contain a ".git" subdirectory # 2. And hasn't been excluded via "third_party/" substring in their path diff --git a/chromium/tools/clang/scripts/build.py b/chromium/tools/clang/scripts/build.py index 78ab2b8ddbd..3d009d1d496 100755 --- a/chromium/tools/clang/scripts/build.py +++ b/chromium/tools/clang/scripts/build.py @@ -52,22 +52,12 @@ BUG_REPORT_URL = ('https://crbug.com and run' win_sdk_dir = None -dia_dll = None def GetWinSDKDir(): - """Get the location of the current SDK. Sets dia_dll as a side-effect.""" + """Get the location of the current SDK.""" global win_sdk_dir - global dia_dll if win_sdk_dir: return win_sdk_dir - # Bump after VC updates. - DIA_DLL = { - '2013': 'msdia120.dll', - '2015': 'msdia140.dll', - '2017': 'msdia140.dll', - '2019': 'msdia140.dll', - } - # Don't let vs_toolchain overwrite our environment. environ_bak = os.environ @@ -85,8 +75,6 @@ def GetWinSDKDir(): vs_path = os.environ['GYP_MSVS_OVERRIDE_PATH'] dia_path = os.path.join(vs_path, 'DIA SDK', 'bin', 'amd64') - dia_dll = os.path.join(dia_path, DIA_DLL[msvs_version]) - os.environ = environ_bak return win_sdk_dir @@ -130,11 +118,6 @@ def CopyFile(src, dst): shutil.copy(src, dst) -def CopyDiaDllTo(target_dir): - GetWinSDKDir() - CopyFile(dia_dll, target_dir) - - def CopyDirectoryContents(src, dst): """Copy the files from directory src to dst.""" dst = os.path.realpath(dst) # realpath() in case dst ends in /.. @@ -253,7 +236,7 @@ def AddGnuWinToPath(): return gnuwin_dir = os.path.join(LLVM_BUILD_TOOLS_DIR, 'gnuwin') - GNUWIN_VERSION = '13' + GNUWIN_VERSION = '14' GNUWIN_STAMP = os.path.join(gnuwin_dir, 'stamp') if ReadStampFile(GNUWIN_STAMP) == GNUWIN_VERSION: print('GNU Win tools already up to date.') @@ -307,6 +290,28 @@ def AddZlibToPath(): return zlib_dir +def DownloadRPMalloc(): + """Download rpmalloc.""" + rpmalloc_dir = os.path.join(LLVM_BUILD_TOOLS_DIR, 'rpmalloc') + if os.path.exists(rpmalloc_dir): + RmTree(rpmalloc_dir) + + # Using rpmalloc bc1923f rather than the latest release (1.4.1) because + # it contains the fix for https://github.com/mjansson/rpmalloc/pull/186 + # which would cause lld to deadlock. + # The zip file was created and uploaded as follows: + # $ mkdir rpmalloc + # $ curl -L https://github.com/mjansson/rpmalloc/archive/bc1923f436539327707b08ef9751a7a87bdd9d2f.tar.gz \ + # | tar -C rpmalloc --strip-components=1 -xzf - + # $ GZIP=-9 tar vzcf rpmalloc-bc1923f.tgz rpmalloc + # $ gsutil.py cp -n -a public-read rpmalloc-bc1923f.tgz \ + # gs://chromium-browser-clang/tools/ + zip_name = 'rpmalloc-bc1923f.tgz' + DownloadAndUnpack(CDS_URL + '/tools/' + zip_name, LLVM_BUILD_TOOLS_DIR) + rpmalloc_dir = rpmalloc_dir.replace('\\', '/') + return rpmalloc_dir + + def MaybeDownloadHostGcc(args): """Download a modern GCC host compiler on Linux.""" if not sys.platform.startswith('linux') or args.gcc_toolchain: @@ -543,6 +548,8 @@ def main(): '-DLLVM_INCLUDE_GO_TESTS=OFF', # TODO(crbug.com/1113475): Update binutils. '-DENABLE_X86_RELAX_RELOCATIONS=NO', + # See crbug.com/1126219: Use native symbolizer instead of DIA + '-DLLVM_ENABLE_DIA_SDK=OFF', ] if args.gcc_toolchain: @@ -584,6 +591,10 @@ def main(): cxxflags.append('-I' + zlib_dir) ldflags.append('-LIBPATH:' + zlib_dir) + # Use rpmalloc. For faster ThinLTO linking. + rpmalloc_dir = DownloadRPMalloc() + base_cmake_args.append('-DLLVM_INTEGRATED_CRT_ALLOC=' + rpmalloc_dir) + if sys.platform != 'win32': # libxml2 is required by the Win manifest merging tool used in cross-builds. base_cmake_args.append('-DLLVM_ENABLE_LIBXML2=FORCE_ON') @@ -661,8 +672,6 @@ def main(): if sys.platform == 'darwin': # TODO(crbug.com/731375): Run check-all on Darwin too. test_targets = [ 'check-llvm', 'check-clang', 'check-builtins' ] - if sys.platform == 'win32': - CopyDiaDllTo(os.path.join(LLVM_BOOTSTRAP_DIR, 'bin')) RunCommand(['ninja'] + test_targets, msvc_arch='x64') RunCommand(['ninja', 'install'], msvc_arch='x64') @@ -1061,8 +1070,6 @@ def main(): RunCommand(['ninja', '-C', LLVM_BUILD_DIR, 'cr-check-all'], msvc_arch='x64') if args.run_tests: - if sys.platform == 'win32': - CopyDiaDllTo(os.path.join(LLVM_BUILD_DIR, 'bin')) test_targets = [ 'check-all' ] if sys.platform == 'darwin': # TODO(thakis): Run check-all on Darwin too, https://crbug.com/959361 diff --git a/chromium/tools/clang/scripts/update.py b/chromium/tools/clang/scripts/update.py index b1018321ec3..4759ce18686 100755 --- a/chromium/tools/clang/scripts/update.py +++ b/chromium/tools/clang/scripts/update.py @@ -38,8 +38,8 @@ import zipfile # https://chromium.googlesource.com/chromium/src/+/master/docs/updating_clang.md # Reverting problematic clang rolls is safe, though. # This is the output of `git describe` and is usable as a commit-ish. -CLANG_REVISION = 'llvmorg-12-init-3492-ga1caa302' -CLANG_SUB_REVISION = 1 +CLANG_REVISION = 'llvmorg-12-init-5035-gd0abc757' +CLANG_SUB_REVISION = 3 PACKAGE_VERSION = '%s-%s' % (CLANG_REVISION, CLANG_SUB_REVISION) RELEASE_VERSION = '12.0.0' diff --git a/chromium/tools/clang/scripts/upload_revision.py b/chromium/tools/clang/scripts/upload_revision.py index 8eb81b3a368..f20cb6abb38 100755 --- a/chromium/tools/clang/scripts/upload_revision.py +++ b/chromium/tools/clang/scripts/upload_revision.py @@ -30,18 +30,18 @@ CHROMIUM_DIR = os.path.abspath(os.path.join(THIS_DIR, '..', '..', '..')) COMMIT_FOOTER = \ ''' Bug: TODO -Cq-Include-Trybots: chromium/try:mac_chromium_asan_rel_ng +Cq-Include-Trybots: chromium/try:chromeos-amd64-generic-cfi-thin-lto-rel +Cq-Include-Trybots: chromium/try:dawn-win10-x86-deps-rel +Cq-Include-Trybots: chromium/try:linux-chromeos-dbg +Cq-Include-Trybots: chromium/try:linux_angle_deqp_rel_ng Cq-Include-Trybots: chromium/try:linux_chromium_cfi_rel_ng Cq-Include-Trybots: chromium/try:linux_chromium_chromeos_asan_rel_ng -Cq-Include-Trybots: chromium/try:linux_chromium_msan_rel_ng Cq-Include-Trybots: chromium/try:linux_chromium_chromeos_msan_rel_ng -Cq-Include-Trybots: chromium/try:linux-chromeos-dbg,win-asan -Cq-Include-Trybots: chromium/try:chromeos-amd64-generic-cfi-thin-lto-rel Cq-Include-Trybots: chromium/try:linux_chromium_compile_dbg_32_ng -Cq-Include-Trybots: chromium/try:win7-rel,win-angle-deqp-rel-32 -Cq-Include-Trybots: chromium/try:linux_angle_deqp_rel_ng +Cq-Include-Trybots: chromium/try:linux_chromium_msan_rel_ng +Cq-Include-Trybots: chromium/try:mac-arm64-rel,mac_chromium_asan_rel_ng Cq-Include-Trybots: chromium/try:win-angle-deqp-rel-64 -Cq-Include-Trybots: chromium/try:dawn-win10-x86-deps-rel +Cq-Include-Trybots: chromium/try:win-asan,win7-rel,win-angle-deqp-rel-32 Cq-Include-Trybots: chrome/try:iphone-device,ipad-device Cq-Include-Trybots: chrome/try:linux-chromeos-chrome Cq-Include-Trybots: chrome/try:win-chrome,win64-chrome,mac-chrome diff --git a/chromium/tools/clang/trace_annotator/CMakeLists.txt b/chromium/tools/clang/trace_annotator/CMakeLists.txt new file mode 100644 index 00000000000..6f59011b79b --- /dev/null +++ b/chromium/tools/clang/trace_annotator/CMakeLists.txt @@ -0,0 +1,26 @@ +set(LLVM_LINK_COMPONENTS + BitReader + MCParser + Option + ) + +add_llvm_executable(trace_annotator + TraceAnnotator.cpp + ) + +target_link_libraries(trace_annotator + clangAST + clangASTMatchers + clangAnalysis + clangBasic + clangDriver + clangEdit + clangFrontend + clangLex + clangParse + clangSema + clangSerialization + clangTooling + ) + +cr_install(TARGETS trace_annotator RUNTIME DESTINATION bin) diff --git a/chromium/tools/clang/trace_annotator/README.md b/chromium/tools/clang/trace_annotator/README.md new file mode 100644 index 00000000000..2e2aa4e418c --- /dev/null +++ b/chromium/tools/clang/trace_annotator/README.md @@ -0,0 +1,109 @@ +# Trace Annotator Tool + +## Introduction + +A tool to annotate functions with ```c++ TRACE_EVENT```. This tool serves two +workflows: + +* Debugging: bulk add traces to all functions in a directory and after finding + the bug remove the traces. +* Adding traces to code: bulk add traces to all functions in a directory, + review the changes carefully and create a patch. + +The goal of this tool is to transfer a function: +```c++ +int foo(int bar, int baz) { + return 42; +} +``` +into: +```c++ +int foo(int bar, int baz) { + TRACE_EVENT0("test", "foo"); + return 42; +} +``` + +In future also argument tracing is to be supported. + +This document is based on //docs/clang_tool_refactoring.md + +## Building + +The following might take approx. 2 hours depending on your computer. + +* Make a new checkout of chromium (suggested, but optional). +* From chromium/src: +* ```shell cr build all``` To make sure all files have been generated. +* ```shell cp -R third_party/llvm-build ~``` +* ```shell tools/clang/scripts/build.py --bootstrap --without-android + --without-fuchsia --extra-tools trace_annotator``` + * TODO how to build with plugin 'find-bad-constructs'? +* ```shell cp -R ~/llvm-build third_party``` This should enable goma support + again. + +### Rebuild just the tool: + +* ```shell cd third_party/llvm-build/Release+Asserts``` +* ```shell ninja trace_annotator``` + +Beware that running ```shell gclient sync``` might overwrite the build and +another full build might be necessary. A backup of the binary from +//third_party/llvm-build/Release+Asserts/bin/trace_annotator might be useful. + +## Testing + +* ```shell tools/clang/scripts/test_tool.py --apply-edits trace_annotator``` + +## Running + +* Chrome plugins are not supported yet, run: ```shell gn args out/Debug/``` and + add: ```clang_use_chrome_plugins = false``` option. +* Make sure you have up to date compilation database: + * To generate it run: ```shell tools/clang/scripts/generate_compdb.py -p + out/Debug/ > out/Debug/compile_commands.json``` + * These are the compiler options for individual files (needed to use the + right version of C++, right library paths...). + +* ```shell DIR="net"; \ + git checkout $DIR && tools/clang/scripts/run_tool.py --tool trace_annotator -p out/Debug/ $DIR \ + | tools/clang/scripts/extract_edits.py \ + | tools/clang/scripts/apply_edits.py -p out/Debug $DIR \ + && git cl format $DIR``` + +* Consult documentation of ```//tools/clang/scripts/run_tool.py``` for more + options. + +### Suggestion: + +Do not run the tool on //base or anything that has to do with tracing or +synchronization. Or at least do not submit the resulting patch. + +### Debugging workflow suggestion: + +* Do some changes. +* ```shell git add . ; git commit``` +* Run the tool. +* ```shell git add . ; git commit``` +* Do some more changes (including fixing a bug). +* ```shell git add . ; git commit``` +* ```shell git rebase -i``` and follow the help. + +### Creating tracing patch suggestion: + +* Run the tool. +* Double check all generated code. +* Add method annotations for methods that are hidden by compiler options (e.g., + if you are on unix then the code in ```c++ #ifdef OS_WIN``` will not be + annotated. + +## TODO + +* Add options: + * Whether to add "do not submit" comment (in upper case). + * Function name formatting (without namespace(s) / getQualifiedNameAsString / + with namespaces but without template tags). + * Category name. + * Make tracing of function arguments. +* Standalone build of the tool (outside of //third_party to avoid overwriting + by ```shell gclient sync```). diff --git a/chromium/tools/clang/trace_annotator/TraceAnnotator.cpp b/chromium/tools/clang/trace_annotator/TraceAnnotator.cpp new file mode 100644 index 00000000000..c1ef48dc838 --- /dev/null +++ b/chromium/tools/clang/trace_annotator/TraceAnnotator.cpp @@ -0,0 +1,208 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// This implements a Clang tool to annotate methods with tracing. It should be +// run using the tools/clang/scripts/run_tool.py helper as described in +// README.md + +#include <string> +#include <vector> +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/ASTMatchers/ASTMatchers.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Frontend/FrontendActions.h" +#include "clang/Tooling/CommonOptionsParser.h" +#include "clang/Tooling/Refactoring.h" +#include "clang/Tooling/Tooling.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/FormatVariadic.h" + +using namespace clang::ast_matchers; +using clang::tooling::CommonOptionsParser; +using clang::tooling::Replacement; +using clang::tooling::Replacements; + +namespace { + +class FunctionDefCallback : public MatchFinder::MatchCallback { + public: + explicit FunctionDefCallback(std::vector<Replacement>* replacements) + : replacements_(replacements) {} + + void run(const MatchFinder::MatchResult& result) override; + + private: + std::vector<Replacement>* const replacements_; +}; + +class TraceAnnotator { + public: + explicit TraceAnnotator(std::vector<Replacement>* replacements) + : function_def_callback_(replacements) {} + + void SetupMatchers(MatchFinder* match_finder); + + private: + FunctionDefCallback function_def_callback_; +}; + +// Given: +// template <typename T, typename T2> void foo(T t, T2 t2) {}; // N1 and N4 +// template <typename T2> void foo<int, T2>(int t, T2 t) {}; // N2 +// template <> void foo<int, char>(int t, char t2) {}; // N3 +// void foo() { +// // This creates implicit template specialization (N4) out of the +// // explicit template definition (N1). +// foo<bool, double>(true, 1.23); +// } +// with the following AST nodes: +// FunctionTemplateDecl foo +// |-FunctionDecl 0x191da68 foo 'void (T, T2)' // N1 +// `-FunctionDecl 0x194bf08 foo 'void (bool, double)' // N4 +// FunctionTemplateDecl foo +// `-FunctionDecl foo 'void (int, T2)' // N2 +// FunctionDecl foo 'void (int, char)' // N3 +// +// Matches AST node N4, but not AST nodes N1, N2 nor N3. +AST_MATCHER(clang::FunctionDecl, isImplicitFunctionTemplateSpecialization) { + switch (Node.getTemplateSpecializationKind()) { + case clang::TSK_ImplicitInstantiation: + return true; + case clang::TSK_Undeclared: + case clang::TSK_ExplicitSpecialization: + case clang::TSK_ExplicitInstantiationDeclaration: + case clang::TSK_ExplicitInstantiationDefinition: + return false; + } +} + +AST_POLYMORPHIC_MATCHER(isInMacroLocation, + AST_POLYMORPHIC_SUPPORTED_TYPES(clang::Decl, + clang::Stmt, + clang::TypeLoc)) { + return Node.getBeginLoc().isMacroID(); +} + +void TraceAnnotator::SetupMatchers(MatchFinder* match_finder) { + const clang::ast_matchers::DeclarationMatcher function_call = + functionDecl( + has(compoundStmt().bind("function body")), + /* Avoid matching the following cases: */ + unless(anyOf( + /* Do not match implicit function template specializations to + avoid conflicting edits. */ + isImplicitFunctionTemplateSpecialization(), + /* Do not match constexpr functions. */ + isConstexpr(), isDefaulted(), + /* Do not match ctor/dtor. */ + cxxConstructorDecl(), cxxDestructorDecl(), + /* Tracing macros can be tricky (e.g., QuicUint128Impl comparison + operators). */ + isInMacroLocation(), has(compoundStmt(isInMacroLocation())), + /* Do not trace lambdas (no name, possbly tracking more parameters + than intended because of [&]). */ + hasParent(cxxRecordDecl(isLambda()))))) + .bind("function"); + match_finder->addMatcher(function_call, &function_def_callback_); +} + +// Returns a string containing the qualified name of the function. Does not +// output template parameters of the function or in case of methods of the +// associated class (as opposed to |function->getQualifiedNameAsString|). +std::string getFunctionName(const clang::FunctionDecl* function) { + std::string qualified_name; + // Add namespace(s) to the name. + if (auto* name_space = llvm::dyn_cast<clang::NamespaceDecl>( + function->getEnclosingNamespaceContext())) { + qualified_name += name_space->getQualifiedNameAsString(); + qualified_name += "::"; + } + // If the function is a method, add class name (without templates). + if (auto* method = llvm::dyn_cast<clang::CXXMethodDecl>(function)) { + qualified_name += method->getParent()->getNameAsString(); + qualified_name += "::"; + } + // Add function name (without templates). + qualified_name += function->getNameAsString(); + return qualified_name; +} + +void FunctionDefCallback::run(const MatchFinder::MatchResult& result) { + const clang::FunctionDecl* function = + result.Nodes.getNodeAs<clang::FunctionDecl>("function"); + // Using this instead of |function->getBody| prevents conflicts with parameter + // names in headers and implementations. + const clang::CompoundStmt* function_body = + result.Nodes.getNodeAs<clang::CompoundStmt>("function body"); + clang::CharSourceRange range = + clang::CharSourceRange::getTokenRange(function_body->getBeginLoc()); + + const char kReplacementTextTemplate[] = R"( TRACE_EVENT0("test", "{0}"); )"; + std::string function_name = getFunctionName(function); + std::string replacement_text = + llvm::formatv(kReplacementTextTemplate, function_name).str(); + + const char kAnnotationTemplate[] = " { {0}"; + std::string annotation = + llvm::formatv(kAnnotationTemplate, replacement_text).str(); + + replacements_->push_back( + Replacement(*result.SourceManager, range, annotation)); +} + +} // namespace + +static llvm::cl::extrahelp common_help(CommonOptionsParser::HelpMessage); + +int main(int argc, const char* argv[]) { + llvm::cl::OptionCategory category("TraceAnnotator Tool"); + CommonOptionsParser options(argc, argv, category); + clang::tooling::ClangTool tool(options.getCompilations(), + options.getSourcePathList()); + + std::vector<Replacement> replacements; + TraceAnnotator converter(&replacements); + MatchFinder match_finder; + converter.SetupMatchers(&match_finder); + + std::unique_ptr<clang::tooling::FrontendActionFactory> frontend_factory = + clang::tooling::newFrontendActionFactory(&match_finder); + int result = tool.run(frontend_factory.get()); + if (result != 0) + return result; + + if (replacements.empty()) + return 0; + + // Each replacement line should have the following format: + // r:<file path>:<offset>:<length>:<replacement text> + // Only the <replacement text> field can contain embedded ":" characters. + // TODO(dcheng): Use a more clever serialization. Ideally we'd use the YAML + // serialization and then use clang-apply-replacements, but that would require + // copying and pasting a larger amount of boilerplate for all Chrome clang + // tools. + + // Keep a set of files where we have already added base_tracing include. + std::set<std::string> include_added_to; + + llvm::outs() << "==== BEGIN EDITS ====\n"; + for (const auto& r : replacements) { + // Add base_tracing import if necessary. + if (include_added_to.find(r.getFilePath().str()) == + include_added_to.end()) { + include_added_to.insert(r.getFilePath().str()); + // Add also copyright so that |test-expected.cc| passes presubmit. + llvm::outs() << "include-user-header:::" << r.getFilePath() + << ":::-1:::-1:::base/trace_event/base_tracing.h" + << "\n"; + } + // Add the actual replacement. + llvm::outs() << "r:::" << r.getFilePath() << ":::" << r.getOffset() + << ":::" << r.getLength() << ":::" << r.getReplacementText() + << "\n"; + } + llvm::outs() << "==== END EDITS ====\n"; + + return 0; +} |