| // 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 |