blob: e1d6c67994c79f61816ab8aae0c26ca6e8bd27f4 [file] [log] [blame]
// Copyright (C) 2023 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <ditto/multithreading_utils.h>
#include <sched.h>
#include <unistd.h>
namespace dittosuite {
bool SchedAttr::IsSet() const { return initialized_; }
void SchedAttr::Set() const {
if (!initialized_) {
LOGF("Setting uninitialized scheduling attributes");
}
LOGD("Setting scheduling policy [" + std::to_string(sched_attr_.sched_policy) +
"] to thread: " + std::to_string(syscall_.GetTid()));
int ret = syscall_.SchedSetattr(0 /* self */, sched_attr_, 0 /* still not implemented */);
if (ret) {
PLOGF("Failed setting scheduling attributes \n" + to_string(sched_attr_) + "\n");
}
}
SchedAttr& SchedAttr::operator=(const dittosuiteproto::SchedAttr& pb) {
typedef dittosuiteproto::SchedAttr::AttributesCase SchedType;
sched_attr_.size = sizeof(sched_attr_);
sched_attr_.sched_flags = pb.flags();
switch (pb.attributes_case()) {
case SchedType::kOther:
switch (pb.other().policy()) {
case dittosuiteproto::SchedAttr::SchedOther::OTHER:
sched_attr_.sched_policy = SchedPolicy::SchedNormal;
break;
case dittosuiteproto::SchedAttr::SchedOther::BATCH:
sched_attr_.sched_policy = SchedPolicy::SchedBatch;
break;
}
sched_attr_.sched_nice = pb.other().nice();
break;
case SchedType::kRt:
switch (pb.rt().policy()) {
case dittosuiteproto::SchedAttr::SchedRt::FIFO:
sched_attr_.sched_policy = SchedPolicy::SchedFifo;
break;
case dittosuiteproto::SchedAttr::SchedRt::RR:
sched_attr_.sched_policy = SchedPolicy::SchedRr;
break;
}
if (pb.rt().priority() < 1 || pb.rt().priority() > 99) {
LOGF("Scheduling priority should be in the range [1, 99]");
}
sched_attr_.sched_priority = pb.rt().priority();
break;
case SchedType::kDeadline:
sched_attr_.sched_policy = SchedPolicy::SchedDeadline;
sched_attr_.sched_runtime = pb.deadline().runtime();
sched_attr_.sched_deadline = pb.deadline().deadline();
sched_attr_.sched_period = pb.deadline().period();
break;
case SchedType::ATTRIBUTES_NOT_SET:
LOGF("Missing scheduling attribute");
break;
}
initialized_ = true;
return *this;
}
void SchedAffinity::Set() const {
if (!initialized_) {
LOGF("Setting uninitialized affinity attributes");
}
LOGD("Setting affinity mask [" + std::to_string(mask_) +
"] to thread: " + std::to_string(syscall_.GetTid()));
cpu_set_t mask;
CPU_ZERO(&mask);
uint64_t tmp_bitset = mask_;
for (unsigned int i=0; i<sizeof(mask_) * 8; ++i) {
if (tmp_bitset & 1) {
LOGD("Enabling on CPU: " + std::to_string(i));
CPU_SET(i, &mask);
}
tmp_bitset >>= 1;
}
int ret = sched_setaffinity(0 /* self */, sizeof(mask), &mask);
if (ret) {
PLOGF("Failed setting scheduling affinity");
}
}
bool SchedAffinity::IsSet() const {
return initialized_;
}
SchedAffinity& SchedAffinity::operator=(const uint64_t mask) {
if (mask == 0) {
LOGF("Empty CPU affinity mask");
}
mask_ = mask;
initialized_ = true;
return *this;
}
/*
* Create a copy of environ and return the new argv[0] size
*
* The stack looks pretty much as follows:
*
* | [...]
* |
* | argc
* | argv[0] (pointer)
* | argv[1] (pointer)
* | ...
* | NULL
* | environ[0] (pointer)
* | environ[1] (pointer)
* | ...
* | NULL
* |
* | [...]
* |
* | *argv[0] (string)
* | *argv[1] (string)
* | ...
* | *environ[0] (string)
* | *environ[1] (string)
* | ...
*
* After this function is call, all the *environ[*] strings will be moved in
* dynamic memory, and the old environ strings space in the stack can be used
* to extend *argv[*].
*/
int move_environ(int argc, char**argv) {
// Count the number of items in environ
int env_last_id = -1;
if (environ) {
while (environ[++env_last_id])
;
}
unsigned int argv_strings_size;
if (env_last_id > 0) {
// If there is something in environ (it exists and there is something more
// than the terminating NULL), size is the total size that will be usable
// for argv after environ is moved. In fact, this would be the size of all
// the argv strings, plus the size of all the current environ strings.
// More specifically, this is:
// - the address of the last element of environ,
// - plus its content size, so now we have the very last byte of environ,
// - subtracted the address of the first string of argv.
argv_strings_size = environ[env_last_id - 1] + strlen(environ[env_last_id - 1]) - argv[0];
} else {
// Otherwise, this is just the size of all the argv strings.
argv_strings_size = argv[argc - 1] + strlen(argv[argc - 1]) - argv[0];
}
if (environ) {
// Create a copy of environ in dynamic memory
char** new_environ = static_cast<char**>(malloc(env_last_id * sizeof(char*)));
// Create a copy in dynamic memory for all the environ strings
unsigned int i = -1;
while (environ[++i]) {
new_environ[i] = strdup(environ[i]);
}
// Also, update the environ pointer
environ = new_environ;
}
return argv_strings_size;
}
/*
* Update the title of the calling process by updating argv[0] (may be
* destructive for the following argv[1..])
*
* If the length of the new title is larger than the previously allocated
* argv[0] in the stack, then this function will overwrite the next argv
* strings.
* If the length of the new title is larger than all the argv strings, this
* function will move environ and use its reclaimed space.
*/
void setproctitle(int argc, char** argv, const char* title) {
size_t available_size = strlen(argv[0]);
size_t argv_size = argv[argc - 1] + strlen(argv[argc - 1]) - argv[0];
if (available_size < argv_size) {
available_size = move_environ(argc, argv);
}
memset(argv[0], 0, available_size);
snprintf(argv[0], available_size - 1, "%s", title);
}
} // namespace dittosuite