-
Notifications
You must be signed in to change notification settings - Fork 4
Rewrite blocking support at runtime & add clang pass #18
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,4 +7,3 @@ venv | |
| third_party/** | ||
| !third_party/CMakeLists.txt | ||
| dist | ||
| folly | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,13 +1,26 @@ | ||
| FROM silkeh/clang:19 AS ltest | ||
|
|
||
| RUN apt update && apt install -y git ninja-build valgrind libboost-context-dev libgflags-dev libstdc++-11-dev | ||
| RUN apt update && apt install -y git ninja-build valgrind libgflags-dev libstdc++-11-dev libclang-19-dev | ||
| RUN mv /usr/lib/gcc/x86_64-linux-gnu/12 /usr/lib/gcc/x86_64-linux-gnu/_12 | ||
|
|
||
| FROM ltest as blocking | ||
| RUN apt install -y pkg-config libcapstone-dev && \ | ||
| RUN apt install -y pkg-config libcapstone-dev libboost-context-dev && \ | ||
| git clone https://github.com/Kirillog/syscall_intercept.git && \ | ||
| cmake syscall_intercept -G Ninja -B syscall_intercept/build -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=clang && \ | ||
| cmake --build syscall_intercept/build --target install | ||
| FROM blocking as folly-blocking | ||
| RUN apt install -y libboost-filesystem-dev libboost-program-options-dev libboost-regex-dev \ | ||
| libdouble-conversion-dev libfast-float-dev libevent-dev libssl-dev libfmt-dev \ | ||
| libgoogle-glog-dev zlib1g-dev && \ | ||
| git clone https://github.com/Kirillog/folly.git && \ | ||
| cmake folly -G Ninja -B folly/build_dir -DCMAKE_BUILD_TYPE=Release | ||
| # cmake --build folly/build_dir --target install | ||
| FROM ltest as userver-blocking | ||
| # userver conflicts with default libboost-context-dev (1.74) version, 1.81 required | ||
| RUN apt install -y python3-dev python3-venv \ | ||
| libboost-context1.81-dev libboost-filesystem1.81-dev libboost-program-options1.81-dev libboost-regex1.81-dev | ||
| libboost-stacktrace1.81-dev libboost-locale1.81-dev \ | ||
| libzstd-dev libyaml-cpp-dev libcrypto++-dev libnghttp2-dev libev-dev | ||
| RUN sh -c "$(wget -O- https://github.com/deluan/zsh-in-docker/releases/download/v1.2.1/zsh-in-docker.sh)" -- \ | ||
| -p git | ||
| CMD [ "zsh" ] | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| #=============================================================================== | ||
| # SETUP CLANG PLUGIN | ||
| #=============================================================================== | ||
| find_package(Clang REQUIRED CONFIG) | ||
| if("${LLVM_VERSION_MAJOR}" VERSION_LESS 19) | ||
| message(FATAL_ERROR "Found LLVM ${LLVM_VERSION_MAJOR}, but need LLVM 19 or above") | ||
| endif() | ||
|
|
||
| include_directories(SYSTEM "${LLVM_INCLUDE_DIRS};${CLANG_INCLUDE_DIRS}") | ||
| #=============================================================================== | ||
| # SETUP CLANG TOOL | ||
| #=============================================================================== | ||
| set(CLANG_TOOL "ClangPassTool") | ||
| set(CLANG_TOOL_SOURCES | ||
| "${CMAKE_CURRENT_SOURCE_DIR}/clangpass_tool.cpp" | ||
| "${CMAKE_CURRENT_SOURCE_DIR}/ast_consumer.cpp" | ||
| "${CMAKE_CURRENT_SOURCE_DIR}/refactor_matcher.cpp" | ||
| ) | ||
|
|
||
| add_executable( | ||
| ${CLANG_TOOL} | ||
| ${CLANG_TOOL_SOURCES} | ||
| ) | ||
|
|
||
| # Configure include directories for 'tool' | ||
| target_include_directories( | ||
| ${CLANG_TOOL} | ||
| PRIVATE | ||
| "${CMAKE_CURRENT_SOURCE_DIR}/include" | ||
| ) | ||
|
|
||
| # Link in the required libraries | ||
| target_link_libraries( | ||
| ${CLANG_TOOL} | ||
| PRIVATE | ||
| clangTooling | ||
| clangToolingRefactoring | ||
| ) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,71 @@ | ||
|
|
||
| #include "include/clangpass.h" | ||
|
|
||
| replace_pass::CodeRefactorASTConsumer::CodeRefactorASTConsumer( | ||
| ASTContext& context, clang::Rewriter& rewriter, | ||
| std::vector<ReplacePair> names, std::string temp_prefix) | ||
| : refactor_handler(context, rewriter, names), | ||
| names(names), | ||
| rewriter(rewriter), | ||
| temp_prefix(std::move(temp_prefix)) { | ||
| auto factory = NameMatcherFactory(); | ||
|
|
||
| for (auto name : names) { | ||
| const auto elaborated_matcher = factory.CreateMatcherFor(name.old_name); | ||
| // Uses previous matcher inside, but returns a wrapping QualifiedTypeLoc | ||
| // node which is used in the function parameters | ||
| const auto qualified_matcher = | ||
| qualifiedTypeLoc(hasUnqualifiedLoc(elaborated_matcher)); | ||
| match_finder.addMatcher( | ||
| elaborated_matcher.bind("ElaboratedTypeLoc" + name.old_name), | ||
| &refactor_handler); | ||
| match_finder.addMatcher( | ||
| qualified_matcher.bind("QualifiedTypeLoc" + name.old_name), | ||
| &refactor_handler); | ||
| } | ||
| } | ||
|
|
||
| std::string replace_pass::CodeRefactorASTConsumer::RefactoredFileName( | ||
| StringRef original_filename) const { | ||
| size_t slash_index = original_filename.rfind("/"); | ||
| // the path should be absolute, so in the worst case we will get '/' as index | ||
| // 0 | ||
| assert(slash_index != std::string::npos); | ||
| slash_index += 1; // include the '/' itself | ||
|
|
||
| std::string path = std::string(original_filename.begin(), | ||
| original_filename.begin() + slash_index); | ||
| std::string filename = std::string(original_filename.begin() + slash_index, | ||
| original_filename.end()); | ||
|
|
||
| return path + temp_prefix + filename; | ||
| } | ||
|
|
||
| void replace_pass::CodeRefactorASTConsumer::HandleTranslationUnit( | ||
| clang::ASTContext& ctx) { | ||
| match_finder.matchAST(ctx); | ||
| const SourceManager& source_manager = rewriter.getSourceMgr(); | ||
| auto fileID = source_manager.getMainFileID(); | ||
| const RewriteBuffer& buffer = rewriter.getEditBuffer(fileID); | ||
|
|
||
| // Output to stdout | ||
| llvm::outs() << "Transformed code:\n"; | ||
| buffer.write(llvm::outs()); | ||
| llvm::outs() << "\n"; | ||
|
|
||
| // Output to file | ||
| const FileEntry* entry = source_manager.getFileEntryForID(fileID); | ||
| std::string new_filename = RefactoredFileName(entry->tryGetRealPathName()); | ||
|
|
||
| std::error_code code; | ||
| llvm::raw_fd_ostream ostream(new_filename, code, llvm::sys::fs::OF_None); | ||
| if (code) { | ||
| llvm::errs() << "Error: Could not open output file: " << code.message() | ||
| << "\n"; | ||
| return; | ||
| } | ||
|
|
||
| llvm::outs() << "Writing to file: " << new_filename << "\n"; | ||
| buffer.write(ostream); | ||
| ostream.close(); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,107 @@ | ||
| //============================================================================== | ||
| // FILE: | ||
| // clangpass_tool.cpp | ||
| // | ||
| // DESCRIPTION: | ||
| // Replaces all ::std:: names usages to custom runtime friendly | ||
| // implementations | ||
| // | ||
| // USAGE: | ||
| // * ./build/bin/clangpass_tool --replace-names=::std::atomic,::std::mutex | ||
| // --insert-names=LTestAtomic,ltest::mutex | ||
| // ./verifying/targets/nonlinear_queue.cpp | ||
| // | ||
| // License: The Unlicense | ||
| //============================================================================== | ||
| #include <clang/Basic/Diagnostic.h> | ||
| #include <clang/Rewrite/Core/Rewriter.h> | ||
| #include <clang/Tooling/Refactoring/Rename/RenamingAction.h> | ||
| #include <clang/Tooling/Refactoring/Rename/USRFindingAction.h> | ||
|
|
||
| #include "clang/Frontend/CompilerInstance.h" | ||
| #include "clang/Tooling/CommonOptionsParser.h" | ||
| #include "clang/Tooling/Refactoring.h" | ||
| #include "include/clangpass.h" | ||
| #include "llvm/Support/CommandLine.h" | ||
|
|
||
| using namespace clang; | ||
| using namespace replace_pass; | ||
|
|
||
| //===----------------------------------------------------------------------===// | ||
| // Command line options | ||
| //===----------------------------------------------------------------------===// | ||
| static cl::OptionCategory CodeRefactorCategory("atomics-replacer options"); | ||
|
|
||
| static cl::opt<std::string> TemporaryPrefix{ | ||
| "temp-prefix", cl::desc("Prefix for temporary files"), cl::init("__tmp_"), | ||
| cl::cat(CodeRefactorCategory)}; | ||
| static cl::list<std::string> ClassNamesToReplace{ | ||
| "replace-names", | ||
| cl::desc("Names of the classes/structs which usages should be renamed"), | ||
| cl::OneOrMore, cl::CommaSeparated, cl::cat(CodeRefactorCategory)}; | ||
| static cl::list<std::string> ClassNamesToInsert{ | ||
| "insert-names", | ||
| cl::desc("Names of the classes/structs which should be used instead"), | ||
| cl::OneOrMore, cl::CommaSeparated, cl::cat(CodeRefactorCategory)}; | ||
|
|
||
| //===----------------------------------------------------------------------===// | ||
| // PluginASTAction | ||
| //===----------------------------------------------------------------------===// | ||
| class CodeRefactorPluginAction : public PluginASTAction { | ||
| public: | ||
| explicit CodeRefactorPluginAction() {} | ||
| // Not used | ||
| bool ParseArgs(const CompilerInstance &compiler, | ||
| const std::vector<std::string> &args) override { | ||
| return true; | ||
| } | ||
|
|
||
| std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &compiler, | ||
| StringRef file) override { | ||
| rewriter.setSourceMgr(compiler.getSourceManager(), compiler.getLangOpts()); | ||
|
|
||
| std::vector<ReplacePair> pairs; | ||
| pairs.reserve(ClassNamesToReplace.size()); | ||
| for (int i = 0; i < ClassNamesToReplace.size(); ++i) { | ||
| pairs.emplace_back(ClassNamesToReplace[i], ClassNamesToInsert[i]); | ||
| } | ||
|
|
||
| return std::make_unique<CodeRefactorASTConsumer>( | ||
| compiler.getASTContext(), rewriter, pairs, TemporaryPrefix); | ||
| } | ||
|
|
||
| private: | ||
| Rewriter rewriter; | ||
| }; | ||
|
|
||
| //===----------------------------------------------------------------------===// | ||
| // Main driver code. | ||
| //===----------------------------------------------------------------------===// | ||
| int main(int argc, const char **argv) { | ||
| Expected<tooling::CommonOptionsParser> options = | ||
| clang::tooling::CommonOptionsParser::create(argc, argv, | ||
| CodeRefactorCategory); | ||
| if (auto E = options.takeError()) { | ||
| errs() << "Problem constructing CommonOptionsParser " | ||
| << toString(std::move(E)) << '\n'; | ||
| return EXIT_FAILURE; | ||
| } | ||
|
|
||
| // auto files = eOptParser->getSourcePathList(); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ? |
||
| // std::vector<const char*> Args = {"clang++", "-E" }; | ||
| // Args.insert(Args.end(), files.begin(), files.end()); | ||
|
|
||
| // auto OptionsParser = clang::tooling::CommonOptionsParser::create( | ||
| // Args.size(), Args.data(), | ||
| // ); | ||
|
|
||
| // clang::tooling::ClangTool Tool(OptionsParser->getCompilations(), | ||
| // OptionsParser->getSourcePathList()); | ||
|
|
||
| clang::tooling::RefactoringTool tool(options->getCompilations(), | ||
| options->getSourcePathList()); | ||
|
|
||
| return tool.run( | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Я правильно понимаю, что предполагается использовать это как два бинаря? Сначала прогоняешь этот, потом загоняешь это в runtime?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nonlinear_queue.cpp -> (clang pass) __tmp_nonlinear_queue.cpp -> (clang++ -fpass-plugin=yieldPass.so) ./nonlinear_queue, который затем уже запускается. Объединить компиляцию с llvm пассом и clang pass нельзя, особенность libTooling API.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Почему нельзя? Ну сделай main, который сделает proc.Exec("clang++ aboba") + запустит потом что нужно
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. В смысле предлагается сделать ltest_clang++, который сначала clang_pass запускает, а потом clang++ -fpass-plugin=yieldPass.so ... ? Честно говоря, clang_pass не настолько готов, чтобы его можно было свободно использовать как часть пайплайна clang-а, который потом будет компилировать большую библиотеку на 1000 таргетов наподобие userver или folly, я бы пока отдельным исполняемым файлом оставил.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Предлагается сделать на бинарь, который внутри просто будет вызывать кланг, в кланг это встраивать не нужно |
||
| clang::tooling::newFrontendActionFactory<CodeRefactorPluginAction>() | ||
| .get()); | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Мы теперь каждый раз будем фолли клонить?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Кажется это чиниться или https://github.com/actions/cache или тем что мы зальем наш образ на условный docker hub и будем его клонить
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Потенциально еще и каждый раз userver пропатченный будем клонить и собирать, вообще хорошо образ на докерхаб залить, я согласен, можно донести это после защит
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Я хз насколько хорошо этот кэш работает, ему по идее нужно на ноде это кэшировать, те на всех раннерах
Докер не поможет особо, только какой-то базовый, тк основной кейс - дебаг нашего кода, те будут правки в рантайме и тд