Skip to content

Commit 07d4303

Browse files
authored
Merge pull request #16444 from BenchR267/feature/add-metrics-to-driver-output
[Driver] Added process information to emitted task messages
2 parents 916b9fd + ac10fb3 commit 07d4303

15 files changed

+352
-97
lines changed

CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -893,6 +893,8 @@ if(HAVE_EL_WGETS)
893893
endif()
894894
cmake_pop_check_state()
895895

896+
check_symbol_exists(wait4 "sys/wait.h" HAVE_WAIT4)
897+
896898
if (LLVM_ENABLE_DOXYGEN)
897899
message(STATUS "Doxygen: enabled")
898900
endif()

docs/DriverParseableOutput.rst

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,12 @@ real process. By using negative numbers for quasi-PIDs, we ensure that no two
5555
jobs will have the same PID: no batch mode job can have the same PID as a real
5656
process. Two jobs with the same PID would violate this uniqueness assumption.
5757

58+
However, if you need to know the real PID of the process, you can use the
59+
'process.real_pid' field in the began/finished/signalled message. It will contain
60+
the process identifier the operating system gave the process.
61+
Be aware that this does not guarantee to be unique as contrasted with "pid"!
62+
If "pid" is greater than 0 it will always match the value in "process.real_pid".
63+
5864

5965
Message Kinds
6066
=============
@@ -76,13 +82,18 @@ key. An "outputs" object will have two fields, a "kind" describing the type of
7682
the output, and a "path" containing the path to the output. A "began" message
7783
will specify the command which was executed under the "command_executable" and
7884
"command_arguments" keys.
85+
To get the real process identifier for the process that started, get the value
86+
of "process.real_pid".
7987

8088
Example::
8189

8290
{
8391
"kind": "began",
8492
"name": "compile",
8593
"pid": 12345,
94+
"process": {
95+
"real_pid": 12345
96+
},
8697
"inputs": ["/src/foo.swift"],
8798
"outputs": [
8899
{
@@ -110,14 +121,24 @@ based messages, it will include the task's PID (or quasi-PID) under the "pid"
110121
key. It will include the exit status of the task under the "exit-status" key. It
111122
may include the stdout/stderr of the task under the "output" key; if this key is
112123
missing, no output was generated by the task.
124+
It will contain the process identifier of the operating system and usage under
125+
the "process" key. The usage is optional and could be omitted.
113126

114127
Example::
115128

116129
{
117130
"kind": "finished",
118131
"name": "compile",
119132
"pid": 12345,
120-
"exit-status": 0
133+
"exit-status": 0,
134+
"process": {
135+
"real_pid": 12345,
136+
"usage": {
137+
"utime": 22740,
138+
"stime": 91107,
139+
"maxrss": 7745536
140+
}
141+
}
121142
// "output" key omitted because there was no stdout/stderr.
122143
}
123144

@@ -131,6 +152,8 @@ the "error-message" key. As with the "finished" message, it may include the
131152
stdout/stderr of the task under the "output" key; if this key is missing, no
132153
output was generated by the task. It may include the "signal" key, the
133154
terminating signal number. (This may not be available on all platforms.)
155+
It will contain the process identifier of the operating system and usage under
156+
the "process" key. The usage is optional and could be omitted.
134157

135158
Example::
136159

@@ -139,7 +162,15 @@ Example::
139162
"name": "compile",
140163
"pid": 12345,
141164
"error-message": "Segmentation fault: 11",
142-
"signal": 4
165+
"signal": 4,
166+
"process": {
167+
"real_pid": 12345,
168+
"usage": {
169+
"utime": 22740,
170+
"stime": 91107,
171+
"maxrss": 7745536
172+
}
173+
}
143174
// "output" key omitted because there was no stdout/stderr.
144175
}
145176

include/swift/Basic/TaskQueue.h

Lines changed: 86 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#ifndef SWIFT_BASIC_TASKQUEUE_H
1414
#define SWIFT_BASIC_TASKQUEUE_H
1515

16+
#include "swift/Basic/JSONSerialization.h"
1617
#include "swift/Basic/LLVM.h"
1718
#include "llvm/ADT/ArrayRef.h"
1819
#include "llvm/Config/config.h"
@@ -22,6 +23,10 @@
2223
#include <memory>
2324
#include <queue>
2425

26+
#if defined(HAVE_GETRUSAGE) && !defined(__HAIKU__)
27+
struct rusage;
28+
#endif
29+
2530
namespace swift {
2631
class UnifiedStatsReporter;
2732
namespace sys {
@@ -39,6 +44,62 @@ enum class TaskFinishedResponse {
3944
StopExecution,
4045
};
4146

47+
/// TaskProcessInformation is bound to a task and contains information about the
48+
/// process that ran this task. This is especially useful to find out which
49+
/// tasks ran in the same process (in multifile-mode or when WMO is activated
50+
/// e.g.). If available, it also contains information about the usage of
51+
/// resources like CPU time or memory the process used in the system. How ever,
52+
/// this could differ from platform to platform and is therefore optional.
53+
54+
/// One process could handle multiple tasks in some modes of the Swift compiler
55+
/// (multifile, WMO). To not break existing tools, the driver does use unique
56+
/// identifiers for the tasks that are not the process identifier. To still be
57+
/// able to reason about tasks that ran in the same process the
58+
/// TaskProcessInformation struct contains information about the actual process
59+
/// of the operating system. The OSPid is the actual process identifier and is
60+
/// therefore not guaranteed to be unique over all tasks. The ProcessUsage
61+
/// contains optional usage information about the operating system process. It
62+
/// could be used by tools that take those information as input for analyzing
63+
/// the Swift compiler on a process-level. It will be `None` if the execution
64+
/// has been skipped or one of the following symbols are not available on the
65+
/// system: `rusage`, `wait4`.
66+
struct TaskProcessInformation {
67+
68+
struct ResourceUsage {
69+
// user time in µs
70+
uint64_t Utime;
71+
// system time in µs
72+
uint64_t Stime;
73+
// maximum resident set size in Bytes
74+
uint64_t Maxrss;
75+
76+
ResourceUsage(uint64_t Utime, uint64_t Stime, uint64_t Maxrss)
77+
: Utime(Utime), Stime(Stime), Maxrss(Maxrss) {}
78+
79+
virtual ~ResourceUsage() = default;
80+
virtual void provideMapping(json::Output &out);
81+
};
82+
83+
private:
84+
// the process identifier of the operating system
85+
ProcessId OSPid;
86+
// usage information about the process, if available
87+
Optional<ResourceUsage> ProcessUsage;
88+
89+
public:
90+
TaskProcessInformation(ProcessId Pid, uint64_t utime, uint64_t stime,
91+
uint64_t maxrss)
92+
: OSPid(Pid), ProcessUsage(ResourceUsage(utime, stime, maxrss)) {}
93+
94+
TaskProcessInformation(ProcessId Pid) : OSPid(Pid), ProcessUsage(None) {}
95+
96+
#if defined(HAVE_GETRUSAGE) && !defined(__HAIKU__)
97+
TaskProcessInformation(ProcessId Pid, struct rusage Usage);
98+
#endif // defined(HAVE_GETRUSAGE) && !defined(__HAIKU__)
99+
virtual ~TaskProcessInformation() = default;
100+
virtual void provideMapping(json::Output &out);
101+
};
102+
42103
/// \brief A class encapsulating the execution of multiple tasks in parallel.
43104
class TaskQueue {
44105
/// Tasks which have not begun execution.
@@ -81,13 +142,15 @@ class TaskQueue {
81142
/// \param Errors the errors from the task which finished execution, if
82143
/// available and SeparateErrors was true. (This may not be available on all
83144
/// platforms.)
145+
/// \param ProcInfo contains information like the operating process identifier
146+
/// and resource usage if available
84147
/// \param Context the context which was passed when the task was added
85148
///
86149
/// \returns true if further execution of tasks should stop,
87150
/// false if execution should continue
88151
using TaskFinishedCallback = std::function<TaskFinishedResponse(
89152
ProcessId Pid, int ReturnCode, StringRef Output, StringRef Errors,
90-
void *Context)>;
153+
TaskProcessInformation ProcInfo, void *Context)>;
91154

92155
/// \brief A callback which will be executed if a task exited abnormally due
93156
/// to a signal.
@@ -100,16 +163,18 @@ class TaskQueue {
100163
/// \param Errors the errors from the task which exited abnormally, if
101164
/// available and SeparateErrors was true. (This may not be available on all
102165
/// platforms.)
166+
/// \param ProcInfo contains information like the operating process identifier
167+
/// and resource usage if available
103168
/// \param Context the context which was passed when the task was added
104-
/// \param Signal the terminating signal number, if available.
105-
/// This may not be available on all platforms. If it is ever provided,
106-
/// it should not be removed in future versions of the compiler.
169+
/// \param Signal the terminating signal number, if available. This may not be
170+
/// available on all platforms. If it is ever provided, it should not be
171+
/// removed in future versions of the compiler.
107172
///
108173
/// \returns a TaskFinishedResponse indicating whether or not execution
109174
/// should proceed
110175
using TaskSignalledCallback = std::function<TaskFinishedResponse(
111176
ProcessId Pid, StringRef ErrorMsg, StringRef Output, StringRef Errors,
112-
void *Context, Optional<int> Signal)>;
177+
void *Context, Optional<int> Signal, TaskProcessInformation ProcInfo)>;
113178
#pragma clang diagnostic pop
114179

115180
/// \brief Indicates whether TaskQueue supports buffering output on the
@@ -202,6 +267,22 @@ class DummyTaskQueue : public TaskQueue {
202267
};
203268

204269
} // end namespace sys
270+
271+
namespace json {
272+
template <> struct ObjectTraits<sys::TaskProcessInformation> {
273+
static void mapping(Output &out, sys::TaskProcessInformation &value) {
274+
value.provideMapping(out);
275+
}
276+
};
277+
278+
template <> struct ObjectTraits<sys::TaskProcessInformation::ResourceUsage> {
279+
static void mapping(Output &out,
280+
sys::TaskProcessInformation::ResourceUsage &value) {
281+
value.provideMapping(out);
282+
}
283+
};
284+
} // end namespace json
285+
205286
} // end namespace swift
206287

207288
#endif // SWIFT_BASIC_TASKQUEUE_H

include/swift/Config.h.in

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88

99
#cmakedefine HAVE_UNICODE_LIBEDIT 1
1010

11+
#cmakedefine HAVE_WAIT4 1
12+
1113
#cmakedefine01 SWIFT_DARWIN_ENABLE_STABLE_ABI_BIT
1214

1315
#endif // SWIFT_CONFIG_H

include/swift/Driver/ParseableOutput.h

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,19 @@ class Job;
2929
namespace parseable_output {
3030

3131
/// \brief Emits a "began" message to the given stream.
32-
void emitBeganMessage(raw_ostream &os, const Job &Cmd, int64_t Pid);
32+
void emitBeganMessage(raw_ostream &os, const Job &Cmd, int64_t Pid,
33+
sys::TaskProcessInformation ProcInfo);
3334

3435
/// \brief Emits a "finished" message to the given stream.
3536
void emitFinishedMessage(raw_ostream &os, const Job &Cmd, int64_t Pid,
36-
int ExitStatus, StringRef Output);
37+
int ExitStatus, StringRef Output,
38+
sys::TaskProcessInformation ProcInfo);
3739

3840
/// \brief Emits a "signalled" message to the given stream.
3941
void emitSignalledMessage(raw_ostream &os, const Job &Cmd, int64_t Pid,
40-
StringRef ErrorMsg, StringRef Output, Optional<int> Signal);
42+
StringRef ErrorMsg, StringRef Output,
43+
Optional<int> Signal,
44+
sys::TaskProcessInformation ProcInfo);
4145

4246
/// \brief Emits a "skipped" message to the given stream.
4347
void emitSkippedMessage(raw_ostream &os, const Job &Cmd);

lib/Basic/Default/TaskQueue.inc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ bool TaskQueue::execute(TaskBeganCallback Began, TaskFinishedCallback Finished,
117117
// a signal during execution.
118118
if (Signalled) {
119119
TaskFinishedResponse Response =
120-
Signalled(PI.Pid, ErrMsg, StringRef(), StringRef(), T->Context, None);
120+
Signalled(PI.Pid, ErrMsg, StringRef(), StringRef(), T->Context, None, TaskProcessInformation(PI.Pid));
121121
ContinueExecution = Response != TaskFinishedResponse::StopExecution;
122122
} else {
123123
// If we don't have a Signalled callback, unconditionally stop.
@@ -128,7 +128,7 @@ bool TaskQueue::execute(TaskBeganCallback Began, TaskFinishedCallback Finished,
128128
// finished.
129129
if (Finished) {
130130
TaskFinishedResponse Response = Finished(PI.Pid, PI.ReturnCode,
131-
StringRef(), StringRef(), T->Context);
131+
StringRef(), StringRef(), TaskProcessInformation(PI.Pid), T->Context);
132132
ContinueExecution = Response != TaskFinishedResponse::StopExecution;
133133
} else if (PI.ReturnCode != 0) {
134134
ContinueExecution = false;

lib/Basic/TaskQueue.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,8 @@ bool DummyTaskQueue::execute(TaskQueue::TaskBeganCallback Began,
8585
std::string Output = "Output placeholder\n";
8686
std::string Errors =
8787
P.second->SeparateErrors ? "Error placeholder\n" : "";
88-
if (Finished(P.first, 0, Output, Errors, P.second->Context) ==
89-
TaskFinishedResponse::StopExecution)
88+
if (Finished(P.first, 0, Output, Errors, TaskProcessInformation(Pid),
89+
P.second->Context) == TaskFinishedResponse::StopExecution)
9090
SubtaskFailed = true;
9191
}
9292
}

0 commit comments

Comments
 (0)