Skip to content

Commit 8b2f6e9

Browse files
committed
[llvm-advisor] Add build coordinator support
This change adds logic to manage builds end to end. It runs the build process, calls the detector, extracts data, and moves generated files to the output directory.
1 parent ebdb0b2 commit 8b2f6e9

File tree

2 files changed

+302
-0
lines changed

2 files changed

+302
-0
lines changed
Lines changed: 257 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,257 @@
1+
#include "CompilationManager.h"
2+
#include "../Detection/UnitDetector.h"
3+
#include "../Utils/FileManager.h"
4+
#include "CommandAnalyzer.h"
5+
#include "DataExtractor.h"
6+
#include "llvm/Support/FileSystem.h"
7+
#include "llvm/Support/Path.h"
8+
#include <chrono>
9+
#include <cstdlib>
10+
#include <set>
11+
12+
namespace llvm {
13+
namespace advisor {
14+
15+
CompilationManager::CompilationManager(const AdvisorConfig &config)
16+
: config_(config), buildExecutor_(config) {
17+
18+
// Get current working directory first
19+
SmallString<256> currentDir;
20+
sys::fs::current_path(currentDir);
21+
initialWorkingDir_ = currentDir.str().str();
22+
23+
// Create temp directory with proper error handling
24+
SmallString<128> tempDirPath;
25+
if (auto EC = sys::fs::createUniqueDirectory("llvm-advisor", tempDirPath)) {
26+
// Use timestamp for temp folder naming
27+
auto now = std::chrono::system_clock::now();
28+
auto timestamp =
29+
std::chrono::duration_cast<std::chrono::seconds>(now.time_since_epoch())
30+
.count();
31+
tempDir_ = "/tmp/llvm-advisor-" + std::to_string(timestamp);
32+
sys::fs::create_directories(tempDir_);
33+
} else {
34+
tempDir_ = tempDirPath.str().str();
35+
}
36+
37+
// Ensure the directory actually exists
38+
if (!sys::fs::exists(tempDir_)) {
39+
sys::fs::create_directories(tempDir_);
40+
}
41+
42+
if (config_.getVerbose()) {
43+
outs() << "Using temporary directory: " << tempDir_ << "\n";
44+
}
45+
}
46+
47+
CompilationManager::~CompilationManager() {
48+
if (!config_.getKeepTemps() && sys::fs::exists(tempDir_)) {
49+
sys::fs::remove_directories(tempDir_);
50+
}
51+
}
52+
53+
Expected<int> CompilationManager::executeWithDataCollection(
54+
const std::string &compiler, const std::vector<std::string> &args) {
55+
56+
// Analyze the build command
57+
BuildContext buildContext = CommandAnalyzer(compiler, args).analyze();
58+
59+
if (config_.getVerbose()) {
60+
outs() << "Build phase: " << static_cast<int>(buildContext.phase) << "\n";
61+
}
62+
63+
// Skip data collection for linking/archiving phases
64+
if (buildContext.phase == BuildPhase::Linking ||
65+
buildContext.phase == BuildPhase::Archiving) {
66+
return buildExecutor_.execute(compiler, args, buildContext, tempDir_);
67+
}
68+
69+
// Detect compilation units
70+
UnitDetector detector(config_);
71+
auto detectedUnits = detector.detectUnits(compiler, args);
72+
if (!detectedUnits) {
73+
return detectedUnits.takeError();
74+
}
75+
76+
std::vector<std::unique_ptr<CompilationUnit>> units;
77+
for (auto &unitInfo : *detectedUnits) {
78+
units.push_back(std::make_unique<CompilationUnit>(unitInfo, tempDir_));
79+
}
80+
81+
// Scan existing files before compilation
82+
auto existingFiles = scanDirectory(initialWorkingDir_);
83+
84+
// Execute compilation with instrumentation
85+
auto execResult =
86+
buildExecutor_.execute(compiler, args, buildContext, tempDir_);
87+
if (!execResult) {
88+
return execResult;
89+
}
90+
int exitCode = *execResult;
91+
92+
// Collect generated files (even if compilation failed for analysis)
93+
collectGeneratedFiles(existingFiles, units);
94+
95+
// Extract additional data
96+
DataExtractor extractor(config_);
97+
for (auto &unit : units) {
98+
if (auto Err = extractor.extractAllData(*unit, tempDir_)) {
99+
if (config_.getVerbose()) {
100+
errs() << "Data extraction failed: " << toString(std::move(Err))
101+
<< "\n";
102+
}
103+
}
104+
}
105+
106+
// Organize output
107+
if (auto Err = organizeOutput(units)) {
108+
if (config_.getVerbose()) {
109+
errs() << "Output organization failed: " << toString(std::move(Err))
110+
<< "\n";
111+
}
112+
}
113+
114+
// Clean up leaked files from source directory
115+
cleanupLeakedFiles();
116+
117+
return exitCode;
118+
}
119+
120+
std::set<std::string>
121+
CompilationManager::scanDirectory(const std::string &dir) const {
122+
std::set<std::string> files;
123+
std::error_code EC;
124+
for (sys::fs::directory_iterator DI(dir, EC), DE; DI != DE && !EC;
125+
DI.increment(EC)) {
126+
if (DI->type() != sys::fs::file_type::directory_file) {
127+
files.insert(DI->path());
128+
}
129+
}
130+
return files;
131+
}
132+
133+
void CompilationManager::collectGeneratedFiles(
134+
const std::set<std::string> &existingFiles,
135+
std::vector<std::unique_ptr<CompilationUnit>> &units) {
136+
FileClassifier classifier;
137+
138+
// Collect files from temp directory
139+
std::error_code EC;
140+
for (sys::fs::recursive_directory_iterator DI(tempDir_, EC), DE;
141+
DI != DE && !EC; DI.increment(EC)) {
142+
if (DI->type() != sys::fs::file_type::directory_file) {
143+
std::string filePath = DI->path();
144+
if (classifier.shouldCollect(filePath)) {
145+
auto classification = classifier.classifyFile(filePath);
146+
147+
// Add to appropriate unit
148+
if (!units.empty()) {
149+
units[0]->addGeneratedFile(classification.category, filePath);
150+
}
151+
}
152+
}
153+
}
154+
155+
// Also check for files that leaked into source directory
156+
auto currentFiles = scanDirectory(initialWorkingDir_);
157+
for (const auto &file : currentFiles) {
158+
if (existingFiles.find(file) == existingFiles.end()) {
159+
if (classifier.shouldCollect(file)) {
160+
auto classification = classifier.classifyFile(file);
161+
162+
// Move leaked file to temp directory
163+
std::string destPath = tempDir_ + "/" + sys::path::filename(file).str();
164+
if (!FileManager::moveFile(file, destPath)) {
165+
if (!units.empty()) {
166+
units[0]->addGeneratedFile(classification.category, destPath);
167+
}
168+
}
169+
}
170+
}
171+
}
172+
}
173+
174+
Error CompilationManager::organizeOutput(
175+
const std::vector<std::unique_ptr<CompilationUnit>> &units) {
176+
// Resolve output directory as absolute path from initial working directory
177+
SmallString<256> outputDirPath;
178+
if (sys::path::is_absolute(config_.getOutputDir())) {
179+
outputDirPath = config_.getOutputDir();
180+
} else {
181+
outputDirPath = initialWorkingDir_;
182+
sys::path::append(outputDirPath, config_.getOutputDir());
183+
}
184+
185+
std::string outputDir = outputDirPath.str().str();
186+
187+
if (config_.getVerbose()) {
188+
outs() << "Output directory: " << outputDir << "\n";
189+
}
190+
191+
// Move collected files to organized structure
192+
for (const auto &unit : units) {
193+
std::string unitDir = outputDir + "/" + unit->getName();
194+
195+
// Remove existing unit directory if it exists
196+
if (sys::fs::exists(unitDir)) {
197+
if (auto EC = sys::fs::remove_directories(unitDir)) {
198+
if (config_.getVerbose()) {
199+
errs() << "Warning: Could not remove existing unit directory: "
200+
<< unitDir << "\n";
201+
}
202+
}
203+
}
204+
205+
// Create fresh unit directory
206+
if (auto EC = sys::fs::create_directories(unitDir)) {
207+
continue; // Skip if we can't create the directory
208+
}
209+
210+
const auto &generatedFiles = unit->getAllGeneratedFiles();
211+
for (const auto &category : generatedFiles) {
212+
std::string categoryDir = unitDir + "/" + category.first;
213+
sys::fs::create_directories(categoryDir);
214+
215+
for (const auto &file : category.second) {
216+
std::string destFile =
217+
categoryDir + "/" + sys::path::filename(file).str();
218+
if (auto Err = FileManager::copyFile(file, destFile)) {
219+
if (config_.getVerbose()) {
220+
errs() << "Failed to copy " << file << " to " << destFile << "\n";
221+
}
222+
}
223+
}
224+
}
225+
}
226+
227+
return Error::success();
228+
}
229+
230+
void CompilationManager::cleanupLeakedFiles() {
231+
FileClassifier classifier;
232+
233+
// Clean up any remaining leaked files in source directory
234+
auto currentFiles = scanDirectory(initialWorkingDir_);
235+
for (const auto &file : currentFiles) {
236+
StringRef filename = sys::path::filename(file);
237+
238+
// Remove optimization remarks files that leaked
239+
if (filename.ends_with(".opt.yaml") || filename.ends_with(".opt.yml")) {
240+
sys::fs::remove(file);
241+
if (config_.getVerbose()) {
242+
outs() << "Cleaned up leaked file: " << file << "\n";
243+
}
244+
}
245+
246+
// Remove profile files that leaked
247+
if (filename.ends_with(".profraw") || filename.ends_with(".profdata")) {
248+
sys::fs::remove(file);
249+
if (config_.getVerbose()) {
250+
outs() << "Cleaned up leaked file: " << file << "\n";
251+
}
252+
}
253+
}
254+
}
255+
256+
} // namespace advisor
257+
} // namespace llvm
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#ifndef LLVM_ADVISOR_COMPILATION_MANAGER_H
2+
#define LLVM_ADVISOR_COMPILATION_MANAGER_H
3+
4+
#include "../Config/AdvisorConfig.h"
5+
#include "../Utils/FileClassifier.h"
6+
#include "BuildExecutor.h"
7+
#include "CompilationUnit.h"
8+
#include "llvm/Support/Error.h"
9+
#include <memory>
10+
#include <set>
11+
#include <vector>
12+
13+
namespace llvm {
14+
namespace advisor {
15+
16+
class CompilationManager {
17+
public:
18+
explicit CompilationManager(const AdvisorConfig &config);
19+
~CompilationManager();
20+
21+
Expected<int> executeWithDataCollection(const std::string &compiler,
22+
const std::vector<std::string> &args);
23+
24+
private:
25+
std::set<std::string> scanDirectory(const std::string &dir) const;
26+
27+
void
28+
collectGeneratedFiles(const std::set<std::string> &existingFiles,
29+
std::vector<std::unique_ptr<CompilationUnit>> &units);
30+
31+
Error
32+
organizeOutput(const std::vector<std::unique_ptr<CompilationUnit>> &units);
33+
34+
void cleanupLeakedFiles();
35+
36+
const AdvisorConfig &config_;
37+
BuildExecutor buildExecutor_;
38+
std::string tempDir_;
39+
std::string initialWorkingDir_;
40+
};
41+
42+
} // namespace advisor
43+
} // namespace llvm
44+
45+
#endif

0 commit comments

Comments
 (0)