linux: allow collecting scheduler priorities to fail

SELinux blocks the handler from collecting these values on Android M.
They should eventually be collected via the broker.

Change-Id: Iad47759b2ebf23148cb5b2c401241ee87f8ffd27
Reviewed-on: https://chromium-review.googlesource.com/1226120
Commit-Queue: Joshua Peraza <jperaza@chromium.org>
Reviewed-by: Mark Mentovai <mark@chromium.org>
This commit is contained in:
Joshua Peraza 2018-09-14 09:29:27 -07:00 committed by Commit Bot
parent 076d760d63
commit 8595f4b423
3 changed files with 127 additions and 105 deletions

View File

@ -60,31 +60,36 @@ bool ProcessReaderLinux::Thread::InitializePtrace(
return false;
}
// TODO(jperaza): Collect scheduling priorities via the broker when they can't
// be collected directly.
have_priorities = false;
// TODO(jperaza): Starting with Linux 3.14, scheduling policy, static
// priority, and nice value can be collected all in one call with
// sched_getattr().
int res = sched_getscheduler(tid);
if (res < 0) {
PLOG(ERROR) << "sched_getscheduler";
return false;
PLOG(WARNING) << "sched_getscheduler";
return true;
}
sched_policy = res;
sched_param param;
if (sched_getparam(tid, &param) != 0) {
PLOG(ERROR) << "sched_getparam";
return false;
PLOG(WARNING) << "sched_getparam";
return true;
}
static_priority = param.sched_priority;
errno = 0;
res = getpriority(PRIO_PROCESS, tid);
if (res == -1 && errno) {
PLOG(ERROR) << "getpriority";
return false;
PLOG(WARNING) << "getpriority";
return true;
}
nice_value = res;
have_priorities = true;
return true;
}

View File

@ -66,6 +66,10 @@ class ProcessReaderLinux {
int static_priority;
int nice_value;
//! \brief `true` if `sched_policy`, `static_priority`, and `nice_value` are
//! all valid.
bool have_priorities;
private:
friend class ProcessReaderLinux;

View File

@ -23,6 +23,109 @@
namespace crashpad {
namespace internal {
namespace {
int ComputeThreadPriority(int static_priority,
int sched_policy,
int nice_value) {
// Map Linux scheduling policy, static priority, and nice value into a
// single int value.
//
// The possible policies in order of approximate priority (low to high) are
// SCHED_IDLE
// SCHED_BATCH
// SCHED_OTHER
// SCHED_RR
// SCHED_FIFO
//
// static_priority is not used for OTHER, BATCH, or IDLE and should be 0.
// For FIFO and RR, static_priority should range from 1 to 99 with 99 being
// the highest priority.
//
// nice value ranges from -20 to 19, with -20 being highest priority
enum class Policy : uint8_t {
kUnknown = 0,
kIdle,
kBatch,
kOther,
kRR,
kFIFO
};
struct LinuxPriority {
#if defined(ARCH_CPU_LITTLE_ENDIAN)
// nice values affect how dynamic priorities are updated, which only
// matters for threads with the same static priority.
uint8_t nice_value = 0;
// The scheduling policy also affects how threads with the same static
// priority are ordered, but has greater impact than nice value.
Policy policy = Policy::kUnknown;
// The static priority is the most significant in determining overall
// priority.
uint8_t static_priority = 0;
// Put this in the most significant byte position to prevent negative
// priorities.
uint8_t unused = 0;
#elif defined(ARCH_CPU_BIG_ENDIAN)
uint8_t unused = 0;
uint8_t static_priority = 0;
Policy policy = Policy::kUnknown;
uint8_t nice_value = 0;
#endif // ARCH_CPU_LITTLE_ENDIAN
};
static_assert(sizeof(LinuxPriority) <= sizeof(int), "priority is too large");
LinuxPriority prio;
// Lower nice values have higher priority, so negate them and add 20 to put
// them in the range 1-40 with 40 being highest priority.
if (nice_value < -20 || nice_value > 19) {
LOG(WARNING) << "invalid nice value " << nice_value;
prio.nice_value = 0;
} else {
prio.nice_value = -1 * nice_value + 20;
}
switch (sched_policy) {
case SCHED_IDLE:
prio.policy = Policy::kIdle;
break;
case SCHED_BATCH:
prio.policy = Policy::kBatch;
break;
case SCHED_OTHER:
prio.policy = Policy::kOther;
break;
case SCHED_RR:
prio.policy = Policy::kRR;
break;
case SCHED_FIFO:
prio.policy = Policy::kFIFO;
break;
default:
prio.policy = Policy::kUnknown;
LOG(WARNING) << "Unknown scheduling policy " << sched_policy;
}
if (static_priority < 0 || static_priority > 99) {
LOG(WARNING) << "invalid static priority " << static_priority;
}
prio.static_priority = static_priority;
int priority;
if (!ReinterpretBytes(prio, &priority)) {
LOG(ERROR) << "Couldn't set priority";
return -1;
}
return priority;
}
} // namespace
ThreadSnapshotLinux::ThreadSnapshotLinux()
: ThreadSnapshot(),
context_union_(),
@ -31,11 +134,9 @@ ThreadSnapshotLinux::ThreadSnapshotLinux()
thread_specific_data_address_(0),
thread_id_(-1),
priority_(-1),
initialized_() {
}
initialized_() {}
ThreadSnapshotLinux::~ThreadSnapshotLinux() {
}
ThreadSnapshotLinux::~ThreadSnapshotLinux() {}
bool ThreadSnapshotLinux::Initialize(ProcessReaderLinux* process_reader,
const ProcessReaderLinux::Thread& thread) {
@ -89,107 +190,19 @@ bool ThreadSnapshotLinux::Initialize(ProcessReaderLinux* process_reader,
#error Port.
#endif
stack_.Initialize(process_reader,
thread.stack_region_address,
thread.stack_region_size);
stack_.Initialize(
process_reader, thread.stack_region_address, thread.stack_region_size);
thread_specific_data_address_ =
thread.thread_info.thread_specific_data_address;
thread_id_ = thread.tid;
// Map Linux scheduling policy, static priority, and nice value into a single
// int value.
//
// The possible policies in order of approximate priority (low to high) are
// SCHED_IDLE
// SCHED_BATCH
// SCHED_OTHER
// SCHED_RR
// SCHED_FIFO
//
// static_priority is not used for OTHER, BATCH, or IDLE and should be 0.
// For FIFO and RR, static_priority should range from 1 to 99 with 99 being
// the highest priority.
//
// nice value ranges from -20 to 19, with -20 being highest priority
enum class Policy : uint8_t {
kUnknown = 0,
kIdle,
kBatch,
kOther,
kRR,
kFIFO
};
struct LinuxPriority {
#if defined(ARCH_CPU_LITTLE_ENDIAN)
// nice values affect how dynamic priorities are updated, which only matters
// for threads with the same static priority.
uint8_t nice_value = 0;
// The scheduling policy also affects how threads with the same static
// priority are ordered, but has greater impact than nice value.
Policy policy = Policy::kUnknown;
// The static priority is the most significant in determining overall
// priority.
uint8_t static_priority = 0;
// Put this in the most significant byte position to prevent negative
// priorities.
uint8_t unused = 0;
#elif defined(ARCH_CPU_BIG_ENDIAN)
uint8_t unused = 0;
uint8_t static_priority = 0;
Policy policy = Policy::kUnknown;
uint8_t nice_value = 0;
#endif // ARCH_CPU_LITTLE_ENDIAN
};
static_assert(sizeof(LinuxPriority) <= sizeof(int), "priority is too large");
LinuxPriority prio;
// Lower nice values have higher priority, so negate them and add 20 to put
// them in the range 1-40 with 40 being highest priority.
if (thread.nice_value < -20 || thread.nice_value > 19) {
LOG(WARNING) << "invalid nice value " << thread.nice_value;
prio.nice_value = 0;
} else {
prio.nice_value = -1 * thread.nice_value + 20;
}
switch (thread.sched_policy) {
case SCHED_IDLE:
prio.policy = Policy::kIdle;
break;
case SCHED_BATCH:
prio.policy = Policy::kBatch;
break;
case SCHED_OTHER:
prio.policy = Policy::kOther;
break;
case SCHED_RR:
prio.policy = Policy::kRR;
break;
case SCHED_FIFO:
prio.policy = Policy::kFIFO;
break;
default:
prio.policy = Policy::kUnknown;
LOG(WARNING) << "Unknown scheduling policy " << thread.sched_policy;
}
if (thread.static_priority < 0 || thread.static_priority > 99) {
LOG(WARNING) << "invalid static priority " << thread.static_priority;
}
prio.static_priority = thread.static_priority;
if (!ReinterpretBytes(prio, &priority_)) {
LOG(ERROR) << "Couldn't set priority";
return false;
}
priority_ =
thread.have_priorities
? ComputeThreadPriority(
thread.static_priority, thread.sched_policy, thread.nice_value)
: -1;
INITIALIZATION_STATE_SET_VALID(initialized_);
return true;