ascii-chat
Binary v0.15.83-53-gb711d8720
Library lib/v0.3.2-701-gb711d8720
Video chat in your terminal
Loading...
Searching...
No Matches
tool.cpp File Reference

Go to the source code of this file.

Functions

std::mutex & outputRegistryMutex ()
std::unordered_set< std::string > & outputRegistry ()
bool registerOutputPath (const std::string &path)
void unregisterOutputPath (const std::string &path)
int main (int argc, const char **argv)

Variables

constexpr unsigned kMacroFlagNone = 0U
constexpr unsigned kMacroFlagExpansion = 1U
constexpr unsigned kMacroFlagInvocation = 2U

Function Documentation

◆ main()

int main ( int argc,
const char ** argv )

Definition at line 705 of file tool.cpp.

705 {
706 // Initialize LLVM infrastructure (this triggers command-line option registration!)
707 llvm::InitLLVM InitLLVM(argc, argv);
708
709 // Parse command-line options
710 cl::ParseCommandLineOptions(argc, argv, "ascii-chat instrumentation tool\n");
711
712 const fs::path outputDir = fs::path(OutputDirectoryOption.getValue());
713 fs::path inputRoot;
714 if (!InputRootOption.getValue().empty()) {
715 inputRoot = fs::path(InputRootOption.getValue());
716 } else {
717 inputRoot = fs::current_path();
718 }
719
720 std::vector<std::string> rawSourceArgs;
721 rawSourceArgs.reserve(SourcePaths.size());
722 for (const auto &path : SourcePaths) {
723 rawSourceArgs.push_back(path);
724 }
725 std::vector<std::string> sourcePaths;
726 std::vector<std::string> extraCompilerArgs;
727 for (std::size_t i = 0; i < rawSourceArgs.size(); ++i) {
728 const std::string &entry = rawSourceArgs[i];
729 if (entry.empty()) {
730 continue;
731 }
732 if (entry == "--") {
733 continue;
734 }
735 if (entry[0] == '-') {
736 extraCompilerArgs.push_back(entry);
737 const bool consumesNext = (entry == "-I" || entry == "-isystem" || entry == "-include" ||
738 entry == "-include-pch" || entry == "-imacros" || entry == "-idirafter" ||
739 entry == "-iprefix" || entry == "-iwithprefix" || entry == "-iwithprefixbefore" ||
740 entry == "-resource-dir" || entry == "-Xclang" || entry == "-Xpreprocessor");
741 if (consumesNext && (i + 1) < rawSourceArgs.size()) {
742 extraCompilerArgs.push_back(rawSourceArgs[i + 1]);
743 ++i;
744 }
745 continue;
746 }
747 sourcePaths.push_back(entry);
748 }
749 if (!FileListOption.getValue().empty()) {
750 std::ifstream listStream(FileListOption.getValue());
751 if (!listStream.is_open()) {
752 llvm::errs() << "Failed to open file list: " << FileListOption.getValue() << "\n";
753 return 1;
754 }
755
756 std::string line;
757 while (std::getline(listStream, line)) {
758 llvm::StringRef trimmed(line);
759 trimmed = trimmed.trim();
760 if (trimmed.empty()) {
761 continue;
762 }
763 sourcePaths.emplace_back(trimmed.str());
764 }
765 }
766
767 if (sourcePaths.empty()) {
768 llvm::errs()
769 << "No translation units specified for instrumentation. Provide positional source paths or --file-list."
770 << "\n";
771 return 1;
772 }
773
774 if (fs::exists(outputDir)) {
775 if (!fs::is_directory(outputDir)) {
776 llvm::errs() << "Output path exists and is not a directory: " << outputDir.c_str() << "\n";
777 return 1;
778 }
779 } else {
780 std::error_code errorCode;
781 fs::create_directories(outputDir, errorCode);
782 if (errorCode) {
783 llvm::errs() << "Failed to create output directory: " << outputDir.c_str() << " - " << errorCode.message()
784 << "\n";
785 return 1;
786 }
787 }
788
789 const bool logMacroExpansions = LogMacroExpansionsOption.getValue() || LegacyIncludeMacroExpansionsOption.getValue();
790 const bool logMacroInvocations = LogMacroInvocationsOption.getValue();
791
792 if (!LogMacroExpansionsOption.getValue() && LegacyIncludeMacroExpansionsOption.getValue()) {
793 llvm::errs() << "warning: --include-macro-expansions is deprecated; use --log-macro-expansions instead\n";
794 }
795
796 // Load compilation database
797 std::string buildPath = BuildPath.getValue();
798 if (buildPath.empty()) {
799 buildPath = ".";
800 }
801 std::string errorMessage;
802 std::unique_ptr<tooling::CompilationDatabase> compilations =
803 tooling::CompilationDatabase::loadFromDirectory(buildPath, errorMessage);
804 if (!compilations) {
805 llvm::errs() << "Error loading compilation database from '" << buildPath << "': " << errorMessage << "\n";
806 return 1;
807 }
808
809 std::unique_ptr<MappedCompilationDatabase> mapped;
810 if (!CompileSourceOption.empty()) {
811 if (sourcePaths.size() != 1) {
812 llvm::errs() << "--compile-source requires exactly one input\n";
813 return 1;
814 }
815 mapped = std::make_unique<MappedCompilationDatabase>(*compilations, CompileSourceOption.getValue());
816 }
817 clang::tooling::ClangTool tool(mapped ? static_cast<const tooling::CompilationDatabase &>(*mapped) : *compilations,
818 sourcePaths);
819
820 if (!ResourceDirOption.empty()) {
821 const std::string resourceDir = ResourceDirOption.getValue();
822 tool.appendArgumentsAdjuster([resourceDir](const tooling::CommandLineArguments &args, StringRef) {
823 auto adjusted = args;
824 adjusted.push_back("-resource-dir");
825 adjusted.push_back(resourceDir);
826 return adjusted;
827 });
828 }
829 if (mapped) {
830 tool.appendArgumentsAdjuster([](const tooling::CommandLineArguments &args, StringRef input) {
831 auto adjusted = args;
832 adjusted.push_back("-DDEFER_PARSING=1");
833 adjusted.push_back("-iquote");
834 adjusted.push_back(fs::path(input.str()).parent_path().string());
835 return adjusted;
836 });
837 }
838
839 if (!extraCompilerArgs.empty()) {
840 const std::vector<std::string> extraArgsCopy(extraCompilerArgs.begin(), extraCompilerArgs.end());
841 tool.appendArgumentsAdjuster([extraArgsCopy](const clang::tooling::CommandLineArguments &args, llvm::StringRef) {
842 clang::tooling::CommandLineArguments adjusted = args;
843 adjusted.insert(adjusted.end(), extraArgsCopy.begin(), extraArgsCopy.end());
844 return adjusted;
845 });
846 }
847
848 auto stripPchAdjuster = [](const clang::tooling::CommandLineArguments &args, llvm::StringRef) {
849 clang::tooling::CommandLineArguments result;
850 const auto containsCMakePch = [](llvm::StringRef value) { return value.contains("cmake_pch"); };
851
852 for (std::size_t i = 0; i < args.size(); ++i) {
853 const std::string &arg = args[i];
854 llvm::StringRef argRef(arg);
855
856 if ((argRef == "-include" || argRef == "--include" || argRef == "-include-pch" || argRef == "--include-pch") &&
857 (i + 1) < args.size()) {
858 if (containsCMakePch(args[i + 1])) {
859 ++i;
860 continue;
861 }
862 }
863
864 if ((argRef.starts_with("-include=") || argRef.starts_with("--include=") || argRef.starts_with("-include-pch=") ||
865 argRef.starts_with("--include-pch=")) &&
866 containsCMakePch(argRef)) {
867 continue;
868 }
869
870 result.push_back(arg);
871 }
872
873 return result;
874 };
875 tool.appendArgumentsAdjuster(stripPchAdjuster);
876
877 auto ensureSourceIncludeAdjuster = [&inputRoot](const clang::tooling::CommandLineArguments &args, llvm::StringRef) {
878 clang::tooling::CommandLineArguments result = args;
879
880 const auto hasInclude = [&result](const std::string &dir) {
881 const std::string combined = "-I" + dir;
882 if (std::find(result.begin(), result.end(), combined) != result.end()) {
883 return true;
884 }
885 for (std::size_t i = 0; i + 1 < result.size(); ++i) {
886 if (result[i] == "-I" && result[i + 1] == dir) {
887 return true;
888 }
889 }
890 return false;
891 };
892
893 const auto appendInclude = [&result](const std::string &dir) {
894 result.push_back("-I");
895 result.push_back(dir);
896 };
897
898 const std::string libDir = (inputRoot / "lib").generic_string();
899 const std::string srcDir = (inputRoot / "src").generic_string();
900
901 if (!hasInclude(libDir)) {
902 appendInclude(libDir);
903 }
904 if (!hasInclude(srcDir)) {
905 appendInclude(srcDir);
906 }
907
908 return result;
909 };
910 tool.appendArgumentsAdjuster(ensureSourceIncludeAdjuster);
911
912 // Instrumented headers are now hard-linked into the output tree, so we can
913 // rely on the original include paths without further adjustment.
914
915 // NOTE: -skip-function-bodies optimization removed - it was causing errors with LibTooling
916 // LibTooling handles arguments differently than the clang driver, so -Xclang flags don't work
917 // The optimization wasn't providing significant benefit anyway
918
919 // Optimization: strip unnecessary compilation flags
920 auto stripUnnecessaryFlags = [](const clang::tooling::CommandLineArguments &args, llvm::StringRef) {
921 clang::tooling::CommandLineArguments result;
922
923 for (size_t i = 0; i < args.size(); ++i) {
924 const std::string &arg = args[i];
925
926 // Skip sanitizer flags (not needed for instrumentation, slow down parsing)
927 if (arg.find("-fsanitize") != std::string::npos)
928 continue;
929 if (arg.find("-fno-sanitize") != std::string::npos)
930 continue;
931 if (arg.find("sanitize") != std::string::npos)
932 continue;
933
934 // Skip debug info generation flags (not needed, slow down codegen)
935 if (arg == "-g" || arg == "-g2" || arg == "-g3")
936 continue;
937 if (arg == "-gcolumn-info")
938 continue;
939 if (arg == "-fstandalone-debug")
940 continue;
941 if (arg.find("-gcodeview") != std::string::npos)
942 continue;
943 if (arg.find("-gdwarf") != std::string::npos)
944 continue;
945
946 // Skip stack protector (not needed for instrumentation)
947 if (arg.find("-fstack-protector") != std::string::npos)
948 continue;
949
950 // Skip frame pointer flags
951 if (arg.find("-fno-omit-frame-pointer") != std::string::npos)
952 continue;
953 if (arg.find("-fomit-frame-pointer") != std::string::npos)
954 continue;
955
956 // Skip optimization-related debug flags
957 if (arg == "-fno-inline")
958 continue;
959 if (arg == "-fno-eliminate-unused-debug-types")
960 continue;
961
962 result.push_back(arg);
963 }
964
965 // NOTE: -w flag removed - it was causing "no such file or directory" errors with LibTooling
966 // Warnings are already suppressed by the stripped flags above
967
968 return result;
969 };
970 tool.appendArgumentsAdjuster(stripUnnecessaryFlags);
971
972 InstrumentationActionFactory actionFactory(outputDir, inputRoot, !FileIncludeFilters.empty(),
973 !FunctionIncludeFilters.empty(), logMacroInvocations, logMacroExpansions);
974 const int executionResult = tool.run(&actionFactory);
975 if (executionResult != 0) {
976 llvm::errs() << "Instrumenter failed with code " << executionResult << "\n";
977 }
978 return executionResult;
979}
action_args_t args
Definition actions.c:78

References args.

◆ outputRegistry()

std::unordered_set< std::string > & outputRegistry ( )

Definition at line 40 of file tool.cpp.

40 {
41 static std::unordered_set<std::string> registry;
42 return registry;
43}

Referenced by registerOutputPath(), and unregisterOutputPath().

◆ outputRegistryMutex()

std::mutex & outputRegistryMutex ( )

Definition at line 35 of file tool.cpp.

35 {
36 static std::mutex mutex;
37 return mutex;
38}

Referenced by registerOutputPath(), and unregisterOutputPath().

◆ registerOutputPath()

bool registerOutputPath ( const std::string & path)

Definition at line 45 of file tool.cpp.

45 {
46 std::lock_guard<std::mutex> guard(outputRegistryMutex());
47 auto &registry = outputRegistry();
48 auto [_, inserted] = registry.insert(path);
49 return inserted;
50}
std::mutex & outputRegistryMutex()
Definition tool.cpp:35
std::unordered_set< std::string > & outputRegistry()
Definition tool.cpp:40

References outputRegistry(), and outputRegistryMutex().

◆ unregisterOutputPath()

void unregisterOutputPath ( const std::string & path)

Definition at line 52 of file tool.cpp.

52 {
53 std::lock_guard<std::mutex> guard(outputRegistryMutex());
54 outputRegistry().erase(path);
55}

References outputRegistry(), and outputRegistryMutex().

Variable Documentation

◆ kMacroFlagExpansion

unsigned kMacroFlagExpansion = 1U
constexpr

Definition at line 115 of file tool.cpp.

◆ kMacroFlagInvocation

unsigned kMacroFlagInvocation = 2U
constexpr

Definition at line 116 of file tool.cpp.

◆ kMacroFlagNone

unsigned kMacroFlagNone = 0U
constexpr

Definition at line 114 of file tool.cpp.