Как перенаправить llvm::outs() в файл?
Я использую некоторые инструменты LLVM (например, llvm-nm
) как статические библиотеки. Т.е. я скопировал исходный код llvm-nm.cpp, переименовал main(..)
в llvm_nm(..)
и скомпилировал его как статическую библиотеку. Я хотел бы переслать стандартный вывод в мой файл.
Я попытался использовать следующий подход:
int out_fd, err_fd;
fpos_t out_pos, err_pos;
// redirect out
fflush(stdout);
fgetpos(stdout, &out_pos);
out_fd = dup(fileno(stdout));
freopen(outFilename, "w", stdout);
// execute
int ret = llvm_nm(argc_, argv_);
// restore output
fflush(stdout);
dup2(out_fd, fileno(stdout));
close(out_fd);
clearerr(stdout);
fsetpos(stdout, &out_pos);
Проблема в том, что он не пересылается (работает, если я добавлю printf()
в исходном коде нм, но не для nm
выход). Я обращаюсь к источнику, и я вижу, что вывод сделан с использованием llvm::outs()
поток:
outs() << "Archive map" << "\n";
И это реализовано следующим образом:
/// outs() - This returns a reference to a raw_ostream for standard output.
00702 /// Use it like: outs() << "foo" << "bar";
00703 raw_ostream &llvm::outs() {
00704 // Set buffer settings to model stdout behavior.
00705 // Delete the file descriptor when the program exits, forcing error
00706 // detection. If you don't want this behavior, don't use outs().
00707 static raw_fd_ostream S(STDOUT_FILENO, true);
00708 return S;
00709 }
Как я могу перенаправить этот вывод в мой файл?
3 ответа
Я понимаю, что это старый вопрос, однако я наткнулся на него, ища некоторую простую информацию для потока llvm outs(), которую я нашел здесь.
Один из примеров, который поставляется с llvm "BrainF", использует его так:
...
raw_ostream *out = &outs();
if (!JIT) {
if (OutputFilename == "") {
std::string base = InputFilename;
if (InputFilename == "-") { base = "a"; }
// Use default filename.
OutputFilename = base+".bc";
}
if (OutputFilename != "-") {
std::error_code EC;
out = new raw_fd_ostream(OutputFilename, EC, sys::fs::F_None);
}
}
...
if (out != &outs())
delete out;
...
Таким образом, кажется, что вы можете безопасно перенаправить.
Примечание: в этом примере OutputFilename/InputFilename являются типами std::string, созданными с помощью библиотеки поддержки библиотеки llvm.
Кажется, нет простого способа сделать это: нет конструктора копирования / присваивания в llvm::raw_fd_ostream
ни в llvm::raw_ostream
, И freopen
Трюк также не работает, потому что целочисленный дескриптор файла используется для инициализации объекта, возвращаемого llvm::outs()
,
Я вижу только один способ - использовать LD_PRELOAD для изменения реализации llvm::outs()
на лету, или аналогичные трюки с линкерами, но для меня это звучит очень глупо. Может быть, пометить оригинальный символ как слабый, а затем переопределить его в вашей библиотеке?
Если вы готовы терпеть неопределенное поведение, вы можете сделать что-то вроде этого:
#include <llvm/Support/raw_ostream.h>
static void hijack_log_line(const std::string &line) {
// Do whatever logging you want here
logInfo("[llvm] {}", line);
}
class hijack_raw_fd_ostream : public llvm::raw_fd_ostream {
public:
explicit hijack_raw_fd_ostream()
: llvm::raw_fd_ostream(-1, false, false) {
}
protected:
void write_impl(const char *Ptr, size_t Size) override {
static std::string CurrentLine;
std::string_view sv(Ptr, Ptr + Size);
for (char ch : sv) {
if (ch == '\n') {
hijack_log_line(CurrentLine);
CurrentLine.clear();
} else {
CurrentLine += ch;
}
}
}
uint64_t current_pos() const override {
llvm::report_fatal_error("current_pos not implemented!");
}
size_t preferred_buffer_size() const override {
return 0;
}
} hijack_stream;
static_assert(sizeof(hijack_raw_fd_ostream) == sizeof(llvm::raw_fd_ostream));
int main(int argc, char **argv) {
// Disable buffering in the LLVM streams
llvm::outs().SetUnbuffered();
llvm::errs().SetUnbuffered();
// NOTE: This is technically undefined behaviour, but it works in practice
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdynamic-class-memaccess"
std::memcpy(&llvm::outs(), &hijack_stream, sizeof(llvm::outs()));
std::memcpy(&llvm::errs(), &hijack_stream, sizeof(llvm::errs()));
#pragma clang diagnostic pop
llvm::outs() << "Hello ";
llvm::outs() << "from LLVM!\n";
return 0;
}
Это работает путем перехвата указателя vtable для перехватаwrite_impl
,current_pos
иpreferred_buffer_size
функции. Строки накапливаются и передаются вhijack_log_line
функция.
Это зависит от деталей реализации, которыеllvm::outs()
используетstatic raw_fd_ostream S;
внутренне. Мы наследуем этот класс, чтобы убедиться, что структура памяти совместима, но она может сломаться, если эта реализация изменится.