Skip to content

[4.2] [BatchMode] <rdar://39981525> Emit parseable output for each job, using quasi-PIDs. #16546

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

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 39 additions & 16 deletions docs/DriverParseableOutput.rst
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,29 @@ This allows the driver to pass as much information as it wants about the ongoing
compilation, and programs which parse this data can decide what to use and what
to ignore.


Quasi-PIDs
==========

In previous versions of this format, certain fields labeled "pid" carry only
legitimate process IDs (PIDs) from the underlying operating system. These PIDs
are used by the driver to associate events that happen to jobs, such as signals
when a job crashes with the file being compiled during that event. Real PIDs are
always positive numbers, in keeping with POSIX semantics and the unsigned
Process ID type on Windows.

In more recent versions of this format, the Swift driver combines multiple jobs
together into batches, running each batch in a single subprocess. In this mode,
the driver writes messages describing each job as before, but assigns to each a
so-called "quasi-PID" for the "pid" field, which does not correspond to an
operating system process ID. Rather, a quasi-PID is a **negative number** that
can be used **like** a PID, both as a unique identifier for each job that was
run and in order to relate messages and tasks together, but it does not denote a
real process. By using negative numbers for quasi-PIDs, we ensure that no two
jobs will have the same PID: no batch mode job can have the same PID as a real
process. Two jobs with the same PID would violate this uniqueness assumption.


Message Kinds
=============

Expand All @@ -46,13 +69,13 @@ Began Message
-------------

A "began" message indicates that a new task began. As with all task-based
messages, it will include the task's PID under the "pid" key. It may specify the
task's inputs as an array of paths under the "inputs" key. It may specify the
task's outputs as an array of objects under the "outputs" key. An "outputs"
object will have two fields, a "kind" describing the type of the output, and a
"path" containing the path to the output. A "began" message will specify the
command which was executed under the "command_executable" and "command_arguments"
keys.
messages, it will include the task's PID (or quasi-PID) under the "pid" key. It
may specify the task's inputs as an array of paths under the "inputs" key. It
may specify the task's outputs as an array of objects under the "outputs"
key. An "outputs" object will have two fields, a "kind" describing the type of
the output, and a "path" containing the path to the output. A "began" message
will specify the command which was executed under the "command_executable" and
"command_arguments" keys.

Example::

Expand Down Expand Up @@ -83,10 +106,10 @@ Finished Message
----------------

A "finished" message indicates that a task finished execution. As with all task-
based messages, it will include the task's PID under the "pid" key. It will
include the exit status of the task under the "exit-status" key. It may include
the stdout/stderr of the task under the "output" key; if this key is missing,
no output was generated by the task.
based messages, it will include the task's PID (or quasi-PID) under the "pid"
key. It will include the exit status of the task under the "exit-status" key. It
may include the stdout/stderr of the task under the "output" key; if this key is
missing, no output was generated by the task.

Example::

Expand All @@ -102,12 +125,12 @@ Signalled Message
-----------------

A "signalled" message indicates that a task exited abnormally due to a signal.
As with all task-based message, it will include the task's PID under the "pid"
key. It may include an error message describing the signal under the
"error-message" key. As with the "finished" message, it may include the
As with all task-based message, it will include the task's PID (or quasi-PID)
under the "pid" key. It may include an error message describing the signal under
the "error-message" key. As with the "finished" message, it may include the
stdout/stderr of the task under the "output" key; if this key is missing, no
output was generated by the task. It may include the "signal" key,
the terminating signal number. (This may not be available on all platforms.)
output was generated by the task. It may include the "signal" key, the
terminating signal number. (This may not be available on all platforms.)

Example::

Expand Down
39 changes: 37 additions & 2 deletions include/swift/Driver/Job.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include "llvm/ADT/StringRef.h"
#include "llvm/Option/Option.h"
#include "llvm/Support/Chrono.h"
#include "llvm/Support/Program.h"
#include "llvm/Support/raw_ostream.h"

#include <memory>
Expand Down Expand Up @@ -237,6 +238,11 @@ class Job {

using EnvironmentVector = std::vector<std::pair<const char *, const char *>>;

/// If positive, contains llvm::ProcessID for a real Job on the host OS. If
/// negative, contains a quasi-PID, which identifies a Job that's a member of
/// a BatchJob _without_ denoting an operating system process.
using PID = int64_t;

private:
/// The action which caused the creation of this Job, and the conditions
/// under which it must be run.
Expand Down Expand Up @@ -328,6 +334,15 @@ class Job {
void printCommandLineAndEnvironment(raw_ostream &Stream,
StringRef Terminator = "\n") const;

/// Call the provided Callback with any Jobs (and their possibly-quasi-PIDs)
/// contained within this Job; if this job is not a BatchJob, just pass \c
/// this and the provided \p OSPid back to the Callback.
virtual void forEachContainedJobAndPID(
llvm::sys::ProcessInfo::ProcessId OSPid,
llvm::function_ref<void(const Job *, Job::PID)> Callback) const {
Callback(this, static_cast<Job::PID>(OSPid));
}

void dump() const LLVM_ATTRIBUTE_USED;

static void printArguments(raw_ostream &Stream,
Expand All @@ -345,18 +360,38 @@ class Job {
/// See ToolChain::jobsAreBatchCombinable for details.

class BatchJob : public Job {
SmallVector<const Job *, 4> CombinedJobs;

/// The set of constituents making up the batch.
const SmallVector<const Job *, 4> CombinedJobs;

/// A negative number to use as the base value for assigning quasi-PID to Jobs
/// in the \c CombinedJobs array. Quasi-PIDs count _down_ from this value.
const Job::PID QuasiPIDBase;

public:
BatchJob(const JobAction &Source, SmallVectorImpl<const Job *> &&Inputs,
std::unique_ptr<CommandOutput> Output, const char *Executable,
llvm::opt::ArgStringList Arguments,
EnvironmentVector ExtraEnvironment,
std::vector<FilelistInfo> Infos,
ArrayRef<const Job *> Combined);
ArrayRef<const Job *> Combined, Job::PID &NextQuasiPID);

ArrayRef<const Job*> getCombinedJobs() const {
return CombinedJobs;
}

/// Call the provided callback for each Job in the batch, passing the
/// corresponding quasi-PID with each Job.
void forEachContainedJobAndPID(
llvm::sys::ProcessInfo::ProcessId OSPid,
llvm::function_ref<void(const Job *, Job::PID)> Callback) const override {
Job::PID QPid = QuasiPIDBase;
assert(QPid < 0);
for (auto const *J : CombinedJobs) {
assert(QPid != std::numeric_limits<Job::PID>::min());
Callback(J, QPid--);
}
}
};

} // end namespace driver
Expand Down
8 changes: 3 additions & 5 deletions include/swift/Driver/ParseableOutput.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,15 @@ class Job;

namespace parseable_output {

using swift::sys::ProcessId;

/// \brief Emits a "began" message to the given stream.
void emitBeganMessage(raw_ostream &os, const Job &Cmd, ProcessId Pid);
void emitBeganMessage(raw_ostream &os, const Job &Cmd, int64_t Pid);

/// \brief Emits a "finished" message to the given stream.
void emitFinishedMessage(raw_ostream &os, const Job &Cmd, ProcessId Pid,
void emitFinishedMessage(raw_ostream &os, const Job &Cmd, int64_t Pid,
int ExitStatus, StringRef Output);

/// \brief Emits a "signalled" message to the given stream.
void emitSignalledMessage(raw_ostream &os, const Job &Cmd, ProcessId Pid,
void emitSignalledMessage(raw_ostream &os, const Job &Cmd, int64_t Pid,
StringRef ErrorMsg, StringRef Output, Optional<int> Signal);

/// \brief Emits a "skipped" message to the given stream.
Expand Down
5 changes: 4 additions & 1 deletion include/swift/Driver/ToolChain.h
Original file line number Diff line number Diff line change
Expand Up @@ -214,8 +214,11 @@ class ToolChain {
/// Construct a \c BatchJob that subsumes the work of a set of Jobs. Any pair
/// of elements in \p Jobs are assumed to satisfy the equivalence relation \c
/// jobsAreBatchCombinable, i.e. they should all be "the same" job in in all
/// ways other than their choices of inputs.
/// ways other than their choices of inputs. The provided \p NextQuasiPID
/// should be a negative number that persists between calls; this method will
/// decrement it to assign quasi-PIDs to each of the \p Jobs passed.
std::unique_ptr<Job> constructBatchJob(ArrayRef<const Job *> Jobs,
int64_t &NextQuasiPID,
Compilation &C) const;

/// Return the default language type to use for the given extension.
Expand Down
36 changes: 29 additions & 7 deletions lib/Driver/Compilation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,16 @@ namespace driver {
/// TaskQueue.
CommandSet BatchJobs;

/// Persistent counter for allocating quasi-PIDs to Jobs combined into
/// BatchJobs. Quasi-PIDs are _negative_ PID-like unique keys used to
/// masquerade BatchJob constituents as (quasi)processes, when writing
/// parseable output to consumers that don't understand the idea of a batch
/// job. They are negative in order to avoid possibly colliding with real
/// PIDs (which are always positive). We start at -1000 here as a crude but
/// harmless hedge against colliding with an errno value that might slip
/// into the stream of real PIDs (say, due to a TaskQueue bug).
int64_t NextBatchQuasiPID = -1000;

/// All jobs which have finished execution or which have been determined
/// that they don't need to run.
CommandSet FinishedCommands;
Expand Down Expand Up @@ -313,6 +323,10 @@ namespace driver {
}
}

bool isBatchJob(const Job *MaybeBatchJob) const {
return BatchJobs.count(MaybeBatchJob) != 0;
}

/// Callback which will be called immediately after a task has started. This
/// callback may be used to provide output indicating that the task began.
void taskBegan(ProcessId Pid, void *Context) {
Expand Down Expand Up @@ -343,7 +357,9 @@ namespace driver {
BeganCmd->printCommandLine(llvm::errs());
break;
case OutputLevel::Parseable:
parseable_output::emitBeganMessage(llvm::errs(), *BeganCmd, Pid);
BeganCmd->forEachContainedJobAndPID(Pid, [&](const Job *J, Job::PID P) {
parseable_output::emitBeganMessage(llvm::errs(), *J, P);
});
break;
}
}
Expand Down Expand Up @@ -493,13 +509,16 @@ namespace driver {
break;
case OutputLevel::Parseable:
// Parseable output was requested.
parseable_output::emitFinishedMessage(llvm::errs(), *FinishedCmd, Pid,
ReturnCode, Output);
FinishedCmd->forEachContainedJobAndPID(
Pid, [&](const Job *J, Job::PID P) {
parseable_output::emitFinishedMessage(llvm::errs(), *J, P,
ReturnCode, Output);
});
break;
}
}

if (BatchJobs.count(FinishedCmd) != 0) {
if (isBatchJob(FinishedCmd)) {
return unpackAndFinishBatch(ReturnCode, Output, Errors,
static_cast<const BatchJob *>(FinishedCmd));
}
Expand Down Expand Up @@ -557,8 +576,11 @@ namespace driver {

if (Comp.Level == OutputLevel::Parseable) {
// Parseable output was requested.
parseable_output::emitSignalledMessage(llvm::errs(), *SignalledCmd,
Pid, ErrorMsg, Output, Signal);
SignalledCmd->forEachContainedJobAndPID(
Pid, [&](const Job *J, Job::PID P) {
parseable_output::emitSignalledMessage(llvm::errs(), *J, P,
ErrorMsg, Output, Signal);
});
} else {
// Otherwise, send the buffered output to stderr, though only if we
// support getting buffered output.
Expand Down Expand Up @@ -741,7 +763,7 @@ namespace driver {
llvm::outs() << "Forming batch job from "
<< Batch.size() << " constituents\n";
auto const &TC = Comp.getToolChain();
auto J = TC.constructBatchJob(Batch, Comp);
auto J = TC.constructBatchJob(Batch, NextBatchQuasiPID, Comp);
if (J)
Batches.push_back(Comp.addJob(std::move(J)));
}
Expand Down
10 changes: 8 additions & 2 deletions lib/Driver/Job.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -409,7 +409,13 @@ BatchJob::BatchJob(const JobAction &Source,
const char *Executable, llvm::opt::ArgStringList Arguments,
EnvironmentVector ExtraEnvironment,
std::vector<FilelistInfo> Infos,
ArrayRef<const Job *> Combined)
ArrayRef<const Job *> Combined, int64_t &NextQuasiPID)
: Job(Source, std::move(Inputs), std::move(Output), Executable, Arguments,
ExtraEnvironment, Infos),
CombinedJobs(Combined.begin(), Combined.end()) {}
CombinedJobs(Combined.begin(), Combined.end()),
QuasiPIDBase(NextQuasiPID) {

assert(QuasiPIDBase < 0);
NextQuasiPID -= CombinedJobs.size();
assert(NextQuasiPID < 0);
}
20 changes: 10 additions & 10 deletions lib/Driver/ParseableOutput.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -155,9 +155,9 @@ class DetailedCommandBasedMessage : public CommandBasedMessage {
};

class TaskBasedMessage : public CommandBasedMessage {
ProcessId Pid;
int64_t Pid;
public:
TaskBasedMessage(StringRef Kind, const Job &Cmd, ProcessId Pid) :
TaskBasedMessage(StringRef Kind, const Job &Cmd, int64_t Pid) :
CommandBasedMessage(Kind, Cmd), Pid(Pid) {}

void provideMapping(swift::json::Output &out) override {
Expand All @@ -167,9 +167,9 @@ class TaskBasedMessage : public CommandBasedMessage {
};

class BeganMessage : public DetailedCommandBasedMessage {
ProcessId Pid;
int64_t Pid;
public:
BeganMessage(const Job &Cmd, ProcessId Pid) :
BeganMessage(const Job &Cmd, int64_t Pid) :
DetailedCommandBasedMessage("began", Cmd), Pid(Pid) {}

void provideMapping(swift::json::Output &out) override {
Expand All @@ -181,7 +181,7 @@ class BeganMessage : public DetailedCommandBasedMessage {
class TaskOutputMessage : public TaskBasedMessage {
std::string Output;
public:
TaskOutputMessage(StringRef Kind, const Job &Cmd, ProcessId Pid,
TaskOutputMessage(StringRef Kind, const Job &Cmd, int64_t Pid,
StringRef Output) : TaskBasedMessage(Kind, Cmd, Pid),
Output(Output) {}

Expand All @@ -194,7 +194,7 @@ class TaskOutputMessage : public TaskBasedMessage {
class FinishedMessage : public TaskOutputMessage {
int ExitStatus;
public:
FinishedMessage(const Job &Cmd, ProcessId Pid, StringRef Output,
FinishedMessage(const Job &Cmd, int64_t Pid, StringRef Output,
int ExitStatus) : TaskOutputMessage("finished", Cmd, Pid,
Output),
ExitStatus(ExitStatus) {}
Expand All @@ -209,7 +209,7 @@ class SignalledMessage : public TaskOutputMessage {
std::string ErrorMsg;
Optional<int> Signal;
public:
SignalledMessage(const Job &Cmd, ProcessId Pid, StringRef Output,
SignalledMessage(const Job &Cmd, int64_t Pid, StringRef Output,
StringRef ErrorMsg, Optional<int> Signal) :
TaskOutputMessage("signalled", Cmd, Pid, Output), ErrorMsg(ErrorMsg),
Signal(Signal) {}
Expand Down Expand Up @@ -253,20 +253,20 @@ static void emitMessage(raw_ostream &os, Message &msg) {
}

void parseable_output::emitBeganMessage(raw_ostream &os,
const Job &Cmd, ProcessId Pid) {
const Job &Cmd, int64_t Pid) {
BeganMessage msg(Cmd, Pid);
emitMessage(os, msg);
}

void parseable_output::emitFinishedMessage(raw_ostream &os,
const Job &Cmd, ProcessId Pid,
const Job &Cmd, int64_t Pid,
int ExitStatus, StringRef Output) {
FinishedMessage msg(Cmd, Pid, Output, ExitStatus);
emitMessage(os, msg);
}

void parseable_output::emitSignalledMessage(raw_ostream &os,
const Job &Cmd, ProcessId Pid,
const Job &Cmd, int64_t Pid,
StringRef ErrorMsg,
StringRef Output,
Optional<int> Signal) {
Expand Down
17 changes: 7 additions & 10 deletions lib/Driver/ToolChain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -310,8 +310,8 @@ static void sortJobsToMatchCompilationInputs(ArrayRef<const Job *> unsortedJobs,
/// on \p BatchJob, to build the \c InvocationInfo.
std::unique_ptr<Job>
ToolChain::constructBatchJob(ArrayRef<const Job *> unsortedJobs,
Compilation &C) const
{
Job::PID &NextQuasiPID,
Compilation &C) const {
if (unsortedJobs.empty())
return nullptr;

Expand All @@ -336,14 +336,11 @@ ToolChain::constructBatchJob(ArrayRef<const Job *> unsortedJobs,
JobContext context{C, inputJobs.getArrayRef(), inputActions.getArrayRef(),
*output, OI};
auto invocationInfo = constructInvocation(*batchCJA, context);
return llvm::make_unique<BatchJob>(*batchCJA,
inputJobs.takeVector(),
std::move(output),
executablePath,
std::move(invocationInfo.Arguments),
std::move(invocationInfo.ExtraEnvironment),
std::move(invocationInfo.FilelistInfos),
sortedJobs);
return llvm::make_unique<BatchJob>(
*batchCJA, inputJobs.takeVector(), std::move(output), executablePath,
std::move(invocationInfo.Arguments),
std::move(invocationInfo.ExtraEnvironment),
std::move(invocationInfo.FilelistInfos), sortedJobs, NextQuasiPID);
}

bool
Expand Down
Loading