Skip to content

Commit 8fb0fe3

Browse files
committed
[BatchMode] <rdar://39981525> Emit parseable output for each job, using quasi-PIDs.
1 parent 19513b0 commit 8fb0fe3

File tree

9 files changed

+238
-96
lines changed

9 files changed

+238
-96
lines changed

docs/DriverParseableOutput.rst

Lines changed: 39 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,29 @@ This allows the driver to pass as much information as it wants about the ongoing
3333
compilation, and programs which parse this data can decide what to use and what
3434
to ignore.
3535

36+
37+
Quasi-PIDs
38+
==========
39+
40+
In previous versions of this format, certain fields labeled "pid" carry only
41+
legitimate process IDs (PIDs) from the underlying operating system. These PIDs
42+
are used by the driver to associate events that happen to jobs, such as signals
43+
when a job crashes with the file being compiled during that event. Real PIDs are
44+
always positive numbers, in keeping with POSIX semantics and the unsigned
45+
Process ID type on Windows.
46+
47+
In more recent versions of this format, the Swift driver combines multiple jobs
48+
together into batches, running each batch in a single subprocess. In this mode,
49+
the driver writes messages describing each job as before, but assigns to each a
50+
so-called "quasi-PID" for the "pid" field, which does not correspond to an
51+
operating system process ID. Rather, a quasi-PID is a **negative number** that
52+
can be used **like** a PID, both as a unique identifier for each job that was
53+
run and in order to relate messages and tasks together, but it does not denote a
54+
real process. By using negative numbers for quasi-PIDs, we ensure that no two
55+
jobs will have the same PID: no batch mode job can have the same PID as a real
56+
process. Two jobs with the same PID would violate this uniqueness assumption.
57+
58+
3659
Message Kinds
3760
=============
3861

@@ -46,13 +69,13 @@ Began Message
4669
-------------
4770

4871
A "began" message indicates that a new task began. As with all task-based
49-
messages, it will include the task's PID under the "pid" key. It may specify the
50-
task's inputs as an array of paths under the "inputs" key. It may specify the
51-
task's outputs as an array of objects under the "outputs" key. An "outputs"
52-
object will have two fields, a "kind" describing the type of the output, and a
53-
"path" containing the path to the output. A "began" message will specify the
54-
command which was executed under the "command_executable" and "command_arguments"
55-
keys.
72+
messages, it will include the task's PID (or quasi-PID) under the "pid" key. It
73+
may specify the task's inputs as an array of paths under the "inputs" key. It
74+
may specify the task's outputs as an array of objects under the "outputs"
75+
key. An "outputs" object will have two fields, a "kind" describing the type of
76+
the output, and a "path" containing the path to the output. A "began" message
77+
will specify the command which was executed under the "command_executable" and
78+
"command_arguments" keys.
5679

5780
Example::
5881

@@ -83,10 +106,10 @@ Finished Message
83106
----------------
84107

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

91114
Example::
92115

@@ -102,12 +125,12 @@ Signalled Message
102125
-----------------
103126

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

112135
Example::
113136

include/swift/Driver/Job.h

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "llvm/ADT/StringRef.h"
2727
#include "llvm/Option/Option.h"
2828
#include "llvm/Support/Chrono.h"
29+
#include "llvm/Support/Program.h"
2930
#include "llvm/Support/raw_ostream.h"
3031

3132
#include <memory>
@@ -237,6 +238,11 @@ class Job {
237238

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

241+
/// If positive, contains llvm::ProcessID for a real Job on the host OS. If
242+
/// negative, contains a quasi-PID, which identifies a Job that's a member of
243+
/// a BatchJob _without_ denoting an operating system process.
244+
using PID = int64_t;
245+
240246
private:
241247
/// The action which caused the creation of this Job, and the conditions
242248
/// under which it must be run.
@@ -328,6 +334,15 @@ class Job {
328334
void printCommandLineAndEnvironment(raw_ostream &Stream,
329335
StringRef Terminator = "\n") const;
330336

337+
/// Call the provided Callback with any Jobs (and their possibly-quasi-PIDs)
338+
/// contained within this Job; if this job is not a BatchJob, just pass \c
339+
/// this and the provided \p OSPid back to the Callback.
340+
virtual void forEachContainedJobAndPID(
341+
llvm::sys::ProcessInfo::ProcessId OSPid,
342+
llvm::function_ref<void(const Job *, Job::PID)> Callback) const {
343+
Callback(this, static_cast<Job::PID>(OSPid));
344+
}
345+
331346
void dump() const LLVM_ATTRIBUTE_USED;
332347

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

347362
class BatchJob : public Job {
348-
SmallVector<const Job *, 4> CombinedJobs;
363+
364+
/// The set of constituents making up the batch.
365+
const SmallVector<const Job *, 4> CombinedJobs;
366+
367+
/// A negative number to use as the base value for assigning quasi-PID to Jobs
368+
/// in the \c CombinedJobs array. Quasi-PIDs count _down_ from this value.
369+
const Job::PID QuasiPIDBase;
370+
349371
public:
350372
BatchJob(const JobAction &Source, SmallVectorImpl<const Job *> &&Inputs,
351373
std::unique_ptr<CommandOutput> Output, const char *Executable,
352374
llvm::opt::ArgStringList Arguments,
353375
EnvironmentVector ExtraEnvironment,
354376
std::vector<FilelistInfo> Infos,
355-
ArrayRef<const Job *> Combined);
377+
ArrayRef<const Job *> Combined, Job::PID &NextQuasiPID);
356378

357379
ArrayRef<const Job*> getCombinedJobs() const {
358380
return CombinedJobs;
359381
}
382+
383+
/// Call the provided callback for each Job in the batch, passing the
384+
/// corresponding quasi-PID with each Job.
385+
void forEachContainedJobAndPID(
386+
llvm::sys::ProcessInfo::ProcessId OSPid,
387+
llvm::function_ref<void(const Job *, Job::PID)> Callback) const override {
388+
Job::PID QPid = QuasiPIDBase;
389+
assert(QPid < 0);
390+
for (auto const *J : CombinedJobs) {
391+
assert(QPid != std::numeric_limits<Job::PID>::min());
392+
Callback(J, QPid--);
393+
}
394+
}
360395
};
361396

362397
} // end namespace driver

include/swift/Driver/ParseableOutput.h

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,17 +28,15 @@ class Job;
2828

2929
namespace parseable_output {
3030

31-
using swift::sys::ProcessId;
32-
3331
/// \brief Emits a "began" message to the given stream.
34-
void emitBeganMessage(raw_ostream &os, const Job &Cmd, ProcessId Pid);
32+
void emitBeganMessage(raw_ostream &os, const Job &Cmd, int64_t Pid);
3533

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

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

4442
/// \brief Emits a "skipped" message to the given stream.

include/swift/Driver/ToolChain.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,8 +214,11 @@ class ToolChain {
214214
/// Construct a \c BatchJob that subsumes the work of a set of Jobs. Any pair
215215
/// of elements in \p Jobs are assumed to satisfy the equivalence relation \c
216216
/// jobsAreBatchCombinable, i.e. they should all be "the same" job in in all
217-
/// ways other than their choices of inputs.
217+
/// ways other than their choices of inputs. The provided \p NextQuasiPID
218+
/// should be a negative number that persists between calls; this method will
219+
/// decrement it to assign quasi-PIDs to each of the \p Jobs passed.
218220
std::unique_ptr<Job> constructBatchJob(ArrayRef<const Job *> Jobs,
221+
int64_t &NextQuasiPID,
219222
Compilation &C) const;
220223

221224
/// Return the default language type to use for the given extension.

lib/Driver/Compilation.cpp

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,16 @@ namespace driver {
174174
/// TaskQueue.
175175
CommandSet BatchJobs;
176176

177+
/// Persistent counter for allocating quasi-PIDs to Jobs combined into
178+
/// BatchJobs. Quasi-PIDs are _negative_ PID-like unique keys used to
179+
/// masquerade BatchJob constituents as (quasi)processes, when writing
180+
/// parseable output to consumers that don't understand the idea of a batch
181+
/// job. They are negative in order to avoid possibly colliding with real
182+
/// PIDs (which are always positive). We start at -1000 here as a crude but
183+
/// harmless hedge against colliding with an errno value that might slip
184+
/// into the stream of real PIDs (say, due to a TaskQueue bug).
185+
int64_t NextBatchQuasiPID = -1000;
186+
177187
/// All jobs which have finished execution or which have been determined
178188
/// that they don't need to run.
179189
CommandSet FinishedCommands;
@@ -313,6 +323,10 @@ namespace driver {
313323
}
314324
}
315325

326+
bool isBatchJob(const Job *MaybeBatchJob) const {
327+
return BatchJobs.count(MaybeBatchJob) != 0;
328+
}
329+
316330
/// Callback which will be called immediately after a task has started. This
317331
/// callback may be used to provide output indicating that the task began.
318332
void taskBegan(ProcessId Pid, void *Context) {
@@ -343,7 +357,9 @@ namespace driver {
343357
BeganCmd->printCommandLine(llvm::errs());
344358
break;
345359
case OutputLevel::Parseable:
346-
parseable_output::emitBeganMessage(llvm::errs(), *BeganCmd, Pid);
360+
BeganCmd->forEachContainedJobAndPID(Pid, [&](const Job *J, Job::PID P) {
361+
parseable_output::emitBeganMessage(llvm::errs(), *J, P);
362+
});
347363
break;
348364
}
349365
}
@@ -493,13 +509,16 @@ namespace driver {
493509
break;
494510
case OutputLevel::Parseable:
495511
// Parseable output was requested.
496-
parseable_output::emitFinishedMessage(llvm::errs(), *FinishedCmd, Pid,
497-
ReturnCode, Output);
512+
FinishedCmd->forEachContainedJobAndPID(
513+
Pid, [&](const Job *J, Job::PID P) {
514+
parseable_output::emitFinishedMessage(llvm::errs(), *J, P,
515+
ReturnCode, Output);
516+
});
498517
break;
499518
}
500519
}
501520

502-
if (BatchJobs.count(FinishedCmd) != 0) {
521+
if (isBatchJob(FinishedCmd)) {
503522
return unpackAndFinishBatch(ReturnCode, Output, Errors,
504523
static_cast<const BatchJob *>(FinishedCmd));
505524
}
@@ -557,8 +576,11 @@ namespace driver {
557576

558577
if (Comp.Level == OutputLevel::Parseable) {
559578
// Parseable output was requested.
560-
parseable_output::emitSignalledMessage(llvm::errs(), *SignalledCmd,
561-
Pid, ErrorMsg, Output, Signal);
579+
SignalledCmd->forEachContainedJobAndPID(
580+
Pid, [&](const Job *J, Job::PID P) {
581+
parseable_output::emitSignalledMessage(llvm::errs(), *J, P,
582+
ErrorMsg, Output, Signal);
583+
});
562584
} else {
563585
// Otherwise, send the buffered output to stderr, though only if we
564586
// support getting buffered output.
@@ -741,7 +763,7 @@ namespace driver {
741763
llvm::outs() << "Forming batch job from "
742764
<< Batch.size() << " constituents\n";
743765
auto const &TC = Comp.getToolChain();
744-
auto J = TC.constructBatchJob(Batch, Comp);
766+
auto J = TC.constructBatchJob(Batch, NextBatchQuasiPID, Comp);
745767
if (J)
746768
Batches.push_back(Comp.addJob(std::move(J)));
747769
}

lib/Driver/Job.cpp

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -409,7 +409,13 @@ BatchJob::BatchJob(const JobAction &Source,
409409
const char *Executable, llvm::opt::ArgStringList Arguments,
410410
EnvironmentVector ExtraEnvironment,
411411
std::vector<FilelistInfo> Infos,
412-
ArrayRef<const Job *> Combined)
412+
ArrayRef<const Job *> Combined, int64_t &NextQuasiPID)
413413
: Job(Source, std::move(Inputs), std::move(Output), Executable, Arguments,
414414
ExtraEnvironment, Infos),
415-
CombinedJobs(Combined.begin(), Combined.end()) {}
415+
CombinedJobs(Combined.begin(), Combined.end()),
416+
QuasiPIDBase(NextQuasiPID) {
417+
418+
assert(QuasiPIDBase < 0);
419+
NextQuasiPID -= CombinedJobs.size();
420+
assert(NextQuasiPID < 0);
421+
}

lib/Driver/ParseableOutput.cpp

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -155,9 +155,9 @@ class DetailedCommandBasedMessage : public CommandBasedMessage {
155155
};
156156

157157
class TaskBasedMessage : public CommandBasedMessage {
158-
ProcessId Pid;
158+
int64_t Pid;
159159
public:
160-
TaskBasedMessage(StringRef Kind, const Job &Cmd, ProcessId Pid) :
160+
TaskBasedMessage(StringRef Kind, const Job &Cmd, int64_t Pid) :
161161
CommandBasedMessage(Kind, Cmd), Pid(Pid) {}
162162

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

169169
class BeganMessage : public DetailedCommandBasedMessage {
170-
ProcessId Pid;
170+
int64_t Pid;
171171
public:
172-
BeganMessage(const Job &Cmd, ProcessId Pid) :
172+
BeganMessage(const Job &Cmd, int64_t Pid) :
173173
DetailedCommandBasedMessage("began", Cmd), Pid(Pid) {}
174174

175175
void provideMapping(swift::json::Output &out) override {
@@ -181,7 +181,7 @@ class BeganMessage : public DetailedCommandBasedMessage {
181181
class TaskOutputMessage : public TaskBasedMessage {
182182
std::string Output;
183183
public:
184-
TaskOutputMessage(StringRef Kind, const Job &Cmd, ProcessId Pid,
184+
TaskOutputMessage(StringRef Kind, const Job &Cmd, int64_t Pid,
185185
StringRef Output) : TaskBasedMessage(Kind, Cmd, Pid),
186186
Output(Output) {}
187187

@@ -194,7 +194,7 @@ class TaskOutputMessage : public TaskBasedMessage {
194194
class FinishedMessage : public TaskOutputMessage {
195195
int ExitStatus;
196196
public:
197-
FinishedMessage(const Job &Cmd, ProcessId Pid, StringRef Output,
197+
FinishedMessage(const Job &Cmd, int64_t Pid, StringRef Output,
198198
int ExitStatus) : TaskOutputMessage("finished", Cmd, Pid,
199199
Output),
200200
ExitStatus(ExitStatus) {}
@@ -209,7 +209,7 @@ class SignalledMessage : public TaskOutputMessage {
209209
std::string ErrorMsg;
210210
Optional<int> Signal;
211211
public:
212-
SignalledMessage(const Job &Cmd, ProcessId Pid, StringRef Output,
212+
SignalledMessage(const Job &Cmd, int64_t Pid, StringRef Output,
213213
StringRef ErrorMsg, Optional<int> Signal) :
214214
TaskOutputMessage("signalled", Cmd, Pid, Output), ErrorMsg(ErrorMsg),
215215
Signal(Signal) {}
@@ -253,20 +253,20 @@ static void emitMessage(raw_ostream &os, Message &msg) {
253253
}
254254

255255
void parseable_output::emitBeganMessage(raw_ostream &os,
256-
const Job &Cmd, ProcessId Pid) {
256+
const Job &Cmd, int64_t Pid) {
257257
BeganMessage msg(Cmd, Pid);
258258
emitMessage(os, msg);
259259
}
260260

261261
void parseable_output::emitFinishedMessage(raw_ostream &os,
262-
const Job &Cmd, ProcessId Pid,
262+
const Job &Cmd, int64_t Pid,
263263
int ExitStatus, StringRef Output) {
264264
FinishedMessage msg(Cmd, Pid, Output, ExitStatus);
265265
emitMessage(os, msg);
266266
}
267267

268268
void parseable_output::emitSignalledMessage(raw_ostream &os,
269-
const Job &Cmd, ProcessId Pid,
269+
const Job &Cmd, int64_t Pid,
270270
StringRef ErrorMsg,
271271
StringRef Output,
272272
Optional<int> Signal) {

lib/Driver/ToolChain.cpp

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -310,8 +310,8 @@ static void sortJobsToMatchCompilationInputs(ArrayRef<const Job *> unsortedJobs,
310310
/// on \p BatchJob, to build the \c InvocationInfo.
311311
std::unique_ptr<Job>
312312
ToolChain::constructBatchJob(ArrayRef<const Job *> unsortedJobs,
313-
Compilation &C) const
314-
{
313+
Job::PID &NextQuasiPID,
314+
Compilation &C) const {
315315
if (unsortedJobs.empty())
316316
return nullptr;
317317

@@ -336,14 +336,11 @@ ToolChain::constructBatchJob(ArrayRef<const Job *> unsortedJobs,
336336
JobContext context{C, inputJobs.getArrayRef(), inputActions.getArrayRef(),
337337
*output, OI};
338338
auto invocationInfo = constructInvocation(*batchCJA, context);
339-
return llvm::make_unique<BatchJob>(*batchCJA,
340-
inputJobs.takeVector(),
341-
std::move(output),
342-
executablePath,
343-
std::move(invocationInfo.Arguments),
344-
std::move(invocationInfo.ExtraEnvironment),
345-
std::move(invocationInfo.FilelistInfos),
346-
sortedJobs);
339+
return llvm::make_unique<BatchJob>(
340+
*batchCJA, inputJobs.takeVector(), std::move(output), executablePath,
341+
std::move(invocationInfo.Arguments),
342+
std::move(invocationInfo.ExtraEnvironment),
343+
std::move(invocationInfo.FilelistInfos), sortedJobs, NextQuasiPID);
347344
}
348345

349346
bool

0 commit comments

Comments
 (0)