mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
When providing the -parseable-output flag to the swift compiler, it will provide json formatted messages about tasks that run. I added some optional usage information in form of user time, system time and maxrss to the output. This can be used by other tools using the compiler to get some insights about time and memory usage. Since the output does not longer match processes run (in batch mode), I also added a real_pid field so the client could reason about jobs that belong together if needed. rdar://39798231
293 lines
9.2 KiB
C++
293 lines
9.2 KiB
C++
//===--- ParseableOutput.cpp - Helpers for parseable output ---------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See https://swift.org/LICENSE.txt for license information
|
|
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "swift/Driver/ParseableOutput.h"
|
|
|
|
#include "swift/Basic/JSONSerialization.h"
|
|
#include "swift/Basic/TaskQueue.h"
|
|
#include "swift/Driver/Action.h"
|
|
#include "swift/Driver/Job.h"
|
|
#include "swift/Frontend/FileTypes.h"
|
|
#include "llvm/Option/Arg.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
|
|
using namespace swift::driver::parseable_output;
|
|
using namespace swift::driver;
|
|
using namespace swift::sys;
|
|
using namespace swift;
|
|
|
|
namespace {
|
|
struct CommandInput {
|
|
std::string Path;
|
|
CommandInput() {}
|
|
CommandInput(StringRef Path) : Path(Path) {}
|
|
};
|
|
|
|
using OutputPair = std::pair<file_types::ID, std::string>;
|
|
} // end anonymous namespace
|
|
|
|
namespace swift {
|
|
namespace json {
|
|
template<>
|
|
struct ScalarTraits<CommandInput> {
|
|
static void output(const CommandInput &value, llvm::raw_ostream &os) {
|
|
os << value.Path;
|
|
}
|
|
static bool mustQuote(StringRef) { return true; }
|
|
};
|
|
|
|
template <> struct ScalarEnumerationTraits<file_types::ID> {
|
|
static void enumeration(Output &out, file_types::ID &value) {
|
|
file_types::forAllTypes([&](file_types::ID ty) {
|
|
std::string typeName = file_types::getTypeName(ty);
|
|
out.enumCase(value, typeName.c_str(), ty);
|
|
});
|
|
}
|
|
};
|
|
|
|
template <> struct ObjectTraits<std::pair<file_types::ID, std::string>> {
|
|
static void mapping(Output &out,
|
|
std::pair<file_types::ID, std::string> &value) {
|
|
out.mapRequired("type", value.first);
|
|
out.mapRequired("path", value.second);
|
|
}
|
|
};
|
|
|
|
template<typename T, unsigned N>
|
|
struct ArrayTraits<SmallVector<T, N>> {
|
|
static size_t size(Output &out, SmallVector<T, N> &seq) {
|
|
return seq.size();
|
|
}
|
|
|
|
static T &element(Output &out, SmallVector<T, N> &seq, size_t index) {
|
|
if (index >= seq.size())
|
|
seq.resize(index+1);
|
|
return seq[index];
|
|
}
|
|
};
|
|
} // namespace json
|
|
} // namespace swift
|
|
|
|
namespace {
|
|
|
|
class Message {
|
|
std::string Kind;
|
|
std::string Name;
|
|
public:
|
|
Message(StringRef Kind, StringRef Name) : Kind(Kind), Name(Name) {}
|
|
virtual ~Message() = default;
|
|
|
|
virtual void provideMapping(swift::json::Output &out) {
|
|
out.mapRequired("kind", Kind);
|
|
out.mapRequired("name", Name);
|
|
}
|
|
};
|
|
|
|
class CommandBasedMessage : public Message {
|
|
public:
|
|
CommandBasedMessage(StringRef Kind, const Job &Cmd) :
|
|
Message(Kind, Cmd.getSource().getClassName()) {}
|
|
};
|
|
|
|
class DetailedCommandBasedMessage : public CommandBasedMessage {
|
|
std::string Executable;
|
|
SmallVector<std::string, 16> Arguments;
|
|
std::string CommandLine;
|
|
SmallVector<CommandInput, 4> Inputs;
|
|
SmallVector<OutputPair, 8> Outputs;
|
|
public:
|
|
DetailedCommandBasedMessage(StringRef Kind, const Job &Cmd) :
|
|
CommandBasedMessage(Kind, Cmd) {
|
|
Executable = Cmd.getExecutable();
|
|
for (const auto &A : Cmd.getArguments()) {
|
|
Arguments.push_back(A);
|
|
}
|
|
llvm::raw_string_ostream wrapper(CommandLine);
|
|
Cmd.printCommandLine(wrapper, "");
|
|
wrapper.flush();
|
|
|
|
for (const Action *A : Cmd.getSource().getInputs()) {
|
|
if (const auto *IA = dyn_cast<InputAction>(A))
|
|
Inputs.push_back(CommandInput(IA->getInputArg().getValue()));
|
|
}
|
|
|
|
for (const Job *J : Cmd.getInputs()) {
|
|
auto OutFiles = J->getOutput().getPrimaryOutputFilenames();
|
|
if (const auto *BJAction = dyn_cast<BackendJobAction>(&Cmd.getSource())) {
|
|
Inputs.push_back(CommandInput(OutFiles[BJAction->getInputIndex()]));
|
|
} else {
|
|
for (const std::string &FileName : OutFiles) {
|
|
Inputs.push_back(CommandInput(FileName));
|
|
}
|
|
}
|
|
}
|
|
|
|
// TODO: set up Outputs appropriately.
|
|
file_types::ID PrimaryOutputType = Cmd.getOutput().getPrimaryOutputType();
|
|
if (PrimaryOutputType != file_types::TY_Nothing) {
|
|
for (const std::string &OutputFileName : Cmd.getOutput().
|
|
getPrimaryOutputFilenames()) {
|
|
Outputs.push_back(OutputPair(PrimaryOutputType, OutputFileName));
|
|
}
|
|
}
|
|
file_types::forAllTypes([&](file_types::ID Ty) {
|
|
for (auto Output : Cmd.getOutput().getAdditionalOutputsForType(Ty)) {
|
|
Outputs.push_back(OutputPair(Ty, Output));
|
|
}
|
|
});
|
|
}
|
|
|
|
void provideMapping(swift::json::Output &out) override {
|
|
Message::provideMapping(out);
|
|
out.mapRequired("command", CommandLine); // Deprecated, do not document
|
|
out.mapRequired("command_executable", Executable);
|
|
out.mapRequired("command_arguments", Arguments);
|
|
out.mapOptional("inputs", Inputs);
|
|
out.mapOptional("outputs", Outputs);
|
|
}
|
|
};
|
|
|
|
class TaskBasedMessage : public CommandBasedMessage {
|
|
int64_t Pid;
|
|
public:
|
|
TaskBasedMessage(StringRef Kind, const Job &Cmd, int64_t Pid) :
|
|
CommandBasedMessage(Kind, Cmd), Pid(Pid) {}
|
|
|
|
void provideMapping(swift::json::Output &out) override {
|
|
CommandBasedMessage::provideMapping(out);
|
|
out.mapRequired("pid", Pid);
|
|
}
|
|
};
|
|
|
|
class BeganMessage : public DetailedCommandBasedMessage {
|
|
int64_t Pid;
|
|
TaskProcessInformation ProcInfo;
|
|
|
|
public:
|
|
BeganMessage(const Job &Cmd, int64_t Pid, TaskProcessInformation ProcInfo)
|
|
: DetailedCommandBasedMessage("began", Cmd), Pid(Pid),
|
|
ProcInfo(ProcInfo) {}
|
|
|
|
void provideMapping(swift::json::Output &out) override {
|
|
DetailedCommandBasedMessage::provideMapping(out);
|
|
out.mapRequired("pid", Pid);
|
|
out.mapRequired("process", ProcInfo);
|
|
}
|
|
};
|
|
|
|
class TaskOutputMessage : public TaskBasedMessage {
|
|
std::string Output;
|
|
TaskProcessInformation ProcInfo;
|
|
|
|
public:
|
|
TaskOutputMessage(StringRef Kind, const Job &Cmd, int64_t Pid,
|
|
StringRef Output, TaskProcessInformation ProcInfo)
|
|
: TaskBasedMessage(Kind, Cmd, Pid), Output(Output), ProcInfo(ProcInfo) {}
|
|
|
|
void provideMapping(swift::json::Output &out) override {
|
|
TaskBasedMessage::provideMapping(out);
|
|
out.mapOptional("output", Output, std::string());
|
|
out.mapRequired("process", ProcInfo);
|
|
}
|
|
};
|
|
|
|
class FinishedMessage : public TaskOutputMessage {
|
|
int ExitStatus;
|
|
public:
|
|
FinishedMessage(const Job &Cmd, int64_t Pid, StringRef Output,
|
|
TaskProcessInformation ProcInfo, int ExitStatus)
|
|
: TaskOutputMessage("finished", Cmd, Pid, Output, ProcInfo),
|
|
ExitStatus(ExitStatus) {}
|
|
|
|
void provideMapping(swift::json::Output &out) override {
|
|
TaskOutputMessage::provideMapping(out);
|
|
out.mapRequired("exit-status", ExitStatus);
|
|
}
|
|
};
|
|
|
|
class SignalledMessage : public TaskOutputMessage {
|
|
std::string ErrorMsg;
|
|
Optional<int> Signal;
|
|
public:
|
|
SignalledMessage(const Job &Cmd, int64_t Pid, StringRef Output,
|
|
StringRef ErrorMsg, Optional<int> Signal,
|
|
TaskProcessInformation ProcInfo)
|
|
: TaskOutputMessage("signalled", Cmd, Pid, Output, ProcInfo),
|
|
ErrorMsg(ErrorMsg), Signal(Signal) {}
|
|
|
|
void provideMapping(swift::json::Output &out) override {
|
|
TaskOutputMessage::provideMapping(out);
|
|
out.mapOptional("error-message", ErrorMsg, std::string());
|
|
out.mapOptional("signal", Signal);
|
|
}
|
|
};
|
|
|
|
class SkippedMessage : public DetailedCommandBasedMessage {
|
|
public:
|
|
SkippedMessage(const Job &Cmd) :
|
|
DetailedCommandBasedMessage("skipped", Cmd) {}
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
namespace swift {
|
|
namespace json {
|
|
|
|
template<>
|
|
struct ObjectTraits<Message> {
|
|
static void mapping(Output &out, Message &msg) {
|
|
msg.provideMapping(out);
|
|
}
|
|
};
|
|
|
|
} // namespace json
|
|
} // namespace swift
|
|
|
|
static void emitMessage(raw_ostream &os, Message &msg) {
|
|
std::string JSONString;
|
|
llvm::raw_string_ostream BufferStream(JSONString);
|
|
json::Output yout(BufferStream);
|
|
yout << msg;
|
|
BufferStream.flush();
|
|
os << JSONString.length() << '\n';
|
|
os << JSONString << '\n';
|
|
}
|
|
|
|
void parseable_output::emitBeganMessage(raw_ostream &os, const Job &Cmd,
|
|
int64_t Pid,
|
|
TaskProcessInformation ProcInfo) {
|
|
BeganMessage msg(Cmd, Pid, ProcInfo);
|
|
emitMessage(os, msg);
|
|
}
|
|
|
|
void parseable_output::emitFinishedMessage(raw_ostream &os, const Job &Cmd,
|
|
int64_t Pid, int ExitStatus,
|
|
StringRef Output,
|
|
TaskProcessInformation ProcInfo) {
|
|
FinishedMessage msg(Cmd, Pid, Output, ProcInfo, ExitStatus);
|
|
emitMessage(os, msg);
|
|
}
|
|
|
|
void parseable_output::emitSignalledMessage(raw_ostream &os, const Job &Cmd,
|
|
int64_t Pid, StringRef ErrorMsg,
|
|
StringRef Output,
|
|
Optional<int> Signal,
|
|
TaskProcessInformation ProcInfo) {
|
|
SignalledMessage msg(Cmd, Pid, Output, ErrorMsg, Signal, ProcInfo);
|
|
emitMessage(os, msg);
|
|
}
|
|
|
|
void parseable_output::emitSkippedMessage(raw_ostream &os, const Job &Cmd) {
|
|
SkippedMessage msg(Cmd);
|
|
emitMessage(os, msg);
|
|
}
|