35
41
#include <signal.h>
36
42
#include <unistd.h>
39
#include <linux/oom.h>
46
* This file can be used to adjust the score used to select which
47
* process should be killed in an out-of-memory (OOM) situation. The
48
* kernel uses this value for a bit-shift opera‐ tion of the process's
49
* oom_score value: valid values are in the range -16 to +15, plus the
50
* special value -17, which disables OOM-killing altogether for this
51
* process. A posi‐ tive score increases the likelihood of this process
52
* being killed by the OOM-killer; a negative score decreases the
55
* The default value for this file is 0; a new process inherits its
56
* parent's oom_adj setting. A process must be privileged
57
* (CAP_SYS_RESOURCE) to update this file.
59
* Since Linux 2.6.36, use of this file is deprecated in favor of
60
* /proc/[pid]/oom_score_adj.
64
static int disableOomKillerValue()
71
return OOM_ADJUST_MIN;
76
return OOM_ADJUST_MAX;
79
static OomAdjuster leastLikelyToBeKilled()
81
// By system default, we set the oom_score_adj of Unity8 to -10 (via lightdm).
82
// As we want to avoid that any app's oom_score_adj or oom_adj is <= Unity8's oom_score_adj,
83
// we choose a default value of -9 for oom_score_adj and 0 for oom_adj.
84
static const int default_value = 0;
86
return OomAdjuster(default_value);
89
static OomAdjuster mostLikelyToBeKilled()
91
return OomAdjuster(maxValue());
94
OomAdjuster(int value) : value(value)
100
return !(value < disableOomKillerValue() || value > maxValue());
103
bool applyForPid(pid_t pid) const
105
auto fn = QString("/proc/%1/oom_adj").arg(pid);
108
if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
111
QTextStream out(&file);
123
* This file can be used to adjust the badness heuristic used to
124
* select which process gets killed in out-of-memory conditions.
126
* The badness heuristic assigns a value to each candidate
127
* task ranging from 0 (never kill) to 1000 (always kill)
128
* to determine which process is targeted. The units are
129
* roughly a proportion along that range of allowed memory
130
* the process may allocate from, based on an estimation of
131
* its current memory and swap use. For example, if a task
132
* is using all allowed memory, its badness score will be
133
* 1000. If it is using half of its allowed memory, its
136
* There is an additional factor included in the badness score: root
137
* processes are given 3% extra memory over other tasks.
139
* The amount of "allowed" memory depends on the context in
140
* which the OOM-killer was called. If it is due to the
141
* memory assigned to the allocating task's cpuset being
142
* exhausted, the allowed memory represents the set of mems
143
* assigned to that cpuset (see cpuset(7)). If it is due
144
* to a mempolicy's node(s) being exhausted, the allowed
145
* memory represents the set of mempolicy nodes. If it is
146
* due to a memory limit (or swap limit) being reached, the
147
* allowed memory is that configured limit. Finally, if it
148
* is due to the entire system being out of memory, the
149
* allowed memory represents all allocatable resources.
151
* The value of oom_score_adj is added to the badness score before it
152
* is used to determine which task to kill. Acceptable values range
153
* from -1000 (OOM_SCORE_ADJ_MIN) to +1000 (OOM_SCORE_ADJ_MAX). This
154
* allows user space to control the preference for OOM-killing,
155
* ranging from always preferring a certain task or completely
156
* disabling it from OOM- killing. The lowest possible value, -1000,
157
* is equivalent to disabling OOM-killing entirely for that task,
158
* since it will always report a badness score of 0.
160
* Consequently, it is very simple for user space to define the amount
161
* of memory to consider for each task. Setting a oom_score_adj value
162
* of +500, for example, is roughly equiv‐ alent to allowing the
163
* remainder of tasks sharing the same system, cpuset, mempolicy, or
164
* memory controller resources to use at least 50% more memory. A
165
* value of -500, on the other hand, would be roughly equivalent to
166
* discounting 50% of the task's allowed memory from being considered
167
* as scoring against the task.
169
* For backward compatibility with previous kernels,
170
* /proc/[pid]/oom_adj can still be used to tune the badness score.
171
* Its value is scaled linearly with oom_score_adj.
173
* Writing to /proc/[pid]/oom_score_adj or
174
* /proc/[pid]/oom_adj will change the other with its
177
struct OomScoreAdjuster
179
static int disableOomKillerValue()
181
return OOM_SCORE_ADJ_MIN;
184
static int minValue()
186
return OOM_SCORE_ADJ_MIN;
189
static int maxValue()
191
return OOM_SCORE_ADJ_MAX;
194
static OomScoreAdjuster leastLikelyToBeKilled()
196
// By system default, we set the oom_score_adj of Unity8 to -10 (via lightdm).
197
// As we want to avoid that any app's oom_score_adj is <= Unity8's oom_score_adj,
198
// we choose a default value of -9, and a default increase of +1.
199
static const int default_value = -9;
200
static const int default_increase = 1;
203
// We could be way more clever here if we knew the distribution
204
// of oom_score_adj values of all app processes. However, we just
205
// make sure that the process is not ignored by the oom killer for now.
206
return OomScoreAdjuster(
207
OomScoreAdjuster::thisProcess().isValid() ?
208
OomScoreAdjuster::thisProcess().value + default_increase :
212
static OomScoreAdjuster mostLikelyToBeKilled()
214
// We avoid extremal values for oom_score_adj. For that, we
215
// set it to 80% of the total available range. If we cannot
216
// determine the oom_score_adj of the current process, i.e.,
217
// Unity8, we substract -200 by default.
218
static const float default_percentage = 0.8;
219
static const int default_decrease = -200;
221
return OomScoreAdjuster(
222
OomScoreAdjuster::thisProcess().isValid() ?
223
(maxValue() - OomScoreAdjuster::thisProcess().value) * default_percentage + OomScoreAdjuster::thisProcess().value :
224
maxValue() - default_decrease);
227
static const OomScoreAdjuster& thisProcess()
229
// Initialize as invalid.
230
static OomScoreAdjuster adjusterForThisProcess(minValue()-1);
232
static std::once_flag query_once;
237
pid_t pid = getpid();
239
auto fn = QString("/proc/%1/oom_score_adj").arg(pid);
242
if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
245
QTextStream in(&file);
246
int value; in >> value;
248
adjusterForThisProcess.value = value;
251
return adjusterForThisProcess;
254
OomScoreAdjuster(int value) : value(value)
260
return !(value < disableOomKillerValue() || value > maxValue());
263
bool applyForPid(pid_t pid) const
265
auto fn = QString("/proc/%1/oom_score_adj").arg(pid);
268
if (!file.open(QIODevice::WriteOnly | QIODevice::Text))
271
QTextStream out(&file);
280
void ensureProcessIsUnlikelyToBeKilled(pid_t pid)
282
if (!OomScoreAdjuster::leastLikelyToBeKilled().applyForPid(pid))
283
if (!OomAdjuster::leastLikelyToBeKilled().applyForPid(pid))
284
LOG("ensureProcessIsUnlikelyToBeKilled failed");
287
void ensureProcessIsLikelyToBeKilled(pid_t pid)
289
if (!OomScoreAdjuster::mostLikelyToBeKilled().applyForPid(pid))
290
if (!OomAdjuster::mostLikelyToBeKilled().applyForPid(pid))
291
LOG("ensureProcessIsLikelyToBeKilled failed");
295
TaskController* TaskController::m_theTaskController = nullptr;
297
TaskController* TaskController::singleton()
299
if (!m_theTaskController) {
300
m_theTaskController = new TaskController();
302
return m_theTaskController;
305
TaskController::TaskController(QObject *parent) :
308
preStartCallback = [](const gchar * appId, gpointer userData) {
310
Q_EMIT TaskController::singleton()->processStartReport(QString(appId), false);
313
startedCallback = [](const gchar * appId, gpointer userData) {
315
pid_t pid = upstart_app_launch_get_primary_pid(appId);
316
ensureProcessIsUnlikelyToBeKilled(pid);
319
stopCallback = [](const gchar * appId, gpointer userData) {
321
Q_EMIT TaskController::singleton()->processStopped(QString(appId), false);
324
focusCallback = [](const gchar * appId, gpointer userData) {
326
pid_t pid = upstart_app_launch_get_primary_pid(appId);
327
ensureProcessIsUnlikelyToBeKilled(pid);
328
Q_EMIT TaskController::singleton()->requestFocus(QString(appId));
331
resumeCallback = [](const gchar * appId, gpointer userData) {
333
Q_EMIT TaskController::singleton()->requestResume(QString(appId));
336
failureCallback = [](const gchar * appId, upstart_app_launch_app_failed_t failureType, gpointer userData) {
338
if (failureType == UPSTART_APP_LAUNCH_APP_FAILED_CRASH) {
339
Q_EMIT TaskController::singleton()->processStopped(QString(appId), true);
340
} else if (failureType == UPSTART_APP_LAUNCH_APP_FAILED_START_FAILURE) {
341
Q_EMIT TaskController::singleton()->processStartReport(QString(appId), true);
343
LOG("TaskController: unknown failure type returned from upstart-app-launch");
345
Q_EMIT TaskController::singleton()->requestResume(QString(appId));
348
upstart_app_launch_observer_add_app_starting(preStartCallback, nullptr);
349
upstart_app_launch_observer_add_app_started(startedCallback, nullptr);
350
upstart_app_launch_observer_add_app_stop(stopCallback, nullptr);
351
upstart_app_launch_observer_add_app_focus(focusCallback, nullptr);
352
upstart_app_launch_observer_add_app_resume(resumeCallback, nullptr);
353
upstart_app_launch_observer_add_app_failed(failureCallback, nullptr);
46
TaskController::TaskController(
48
const QSharedPointer<ApplicationController>& appController,
49
const QSharedPointer<ProcessController>& processController) :
51
m_appController(appController),
52
m_processController(processController)
54
connect(m_appController.data(),
55
&ApplicationController::applicationAboutToBeStarted,
57
&TaskController::onApplicationAboutToBeStarted);
59
connect(m_appController.data(),
60
&ApplicationController::applicationStarted,
62
&TaskController::onApplicationStarted);
64
connect(m_appController.data(),
65
&ApplicationController::applicationStopped,
67
&TaskController::onApplicationStopped);
69
connect(m_appController.data(),
70
&ApplicationController::applicationFocusRequest,
72
&TaskController::onApplicationFocusRequest);
74
connect(m_appController.data(),
75
&ApplicationController::applicationResumeRequest,
77
&TaskController::onApplicationResumeRequest);
79
connect(m_appController.data(),
80
&ApplicationController::applicationError,
82
&TaskController::onApplicationError);
356
85
TaskController::~TaskController()
358
upstart_app_launch_observer_delete_app_starting(preStartCallback, nullptr);
359
upstart_app_launch_observer_delete_app_started(startedCallback, nullptr);
360
upstart_app_launch_observer_delete_app_stop(stopCallback, nullptr);
361
upstart_app_launch_observer_delete_app_focus(focusCallback, nullptr);
362
upstart_app_launch_observer_delete_app_resume(resumeCallback, nullptr);
363
upstart_app_launch_observer_delete_app_failed(failureCallback, nullptr);
366
89
bool TaskController::start(const QString& appId, const QStringList& arguments)
368
91
DLOG("TaskController::start appId='%s'", qPrintable(appId));
369
gchar ** upstartArgs = nullptr;
372
// Convert arguments QStringList into format suitable for upstart-app-launch
373
upstartArgs = g_new0(gchar *, arguments.length());
375
for (int i=0; i<arguments.length(); i++) {
376
upstartArgs[i] = arguments.at(i).toLatin1().data();
379
result = upstart_app_launch_start_application(appId.toLatin1().constData(),
380
static_cast<const gchar * const *>(upstartArgs));
383
DLOG_IF(!result, "TaskController::startApplication appId='%s' FAILED", qPrintable(appId));
92
return m_appController->startApplicationWithAppIdAndArgs(appId, arguments);
387
95
bool TaskController::stop(const QString& appId)
389
97
DLOG("TaskController::stop appId='%s'", qPrintable(appId));
392
result = upstart_app_launch_stop_application(appId.toLatin1().constData());
98
auto result = m_appController->stopApplicationWithAppId(appId);
394
99
DLOG_IF(!result, "TaskController::stopApplication appId='%s' FAILED", qPrintable(appId));