| // Copyright 2019 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "sched.h" |
| |
| #include <fuchsia/scheduler/cpp/fidl.h> |
| #include <fuchsia/sys/cpp/fidl.h> |
| #include <lib/async-loop/cpp/loop.h> |
| #include <lib/async-loop/default.h> |
| #include <lib/fdio/fd.h> |
| #include <lib/fdio/limits.h> |
| #include <lib/fdio/spawn.h> |
| #include <lib/sys/cpp/component_context.h> |
| #include <stdio.h> |
| #include <unistd.h> |
| #include <zircon/status.h> |
| #include <zircon/syscalls.h> |
| |
| #include <array> |
| #include <string> |
| #include <utility> |
| |
| #include "args.h" |
| |
| namespace sched { |
| |
| zx_status_t CreateProfile(uint32_t priority, const std::string& name, zx::profile* profile) { |
| std::unique_ptr<sys::ComponentContext> startup_context = |
| sys::ComponentContext::CreateAndServeOutgoingDirectory(); |
| if (startup_context == nullptr) { |
| return ZX_ERR_UNAVAILABLE; |
| } |
| fuchsia::scheduler::ProfileProviderSyncPtr profile_provider; |
| zx_status_t status = startup_context->svc()->Connect(profile_provider.NewRequest()); |
| if (status != ZX_OK) { |
| return status; |
| } |
| zx_status_t server_status; |
| status = profile_provider->GetProfile(priority, name, &server_status, profile); |
| if (status != ZX_OK) { |
| return status; |
| } |
| return server_status; |
| } |
| |
| zx_status_t Launch(zx_handle_t job, const std::vector<std::string>& args, zx::process* process_out, |
| std::string* error_message) { |
| std::array<char, FDIO_SPAWN_ERR_MSG_MAX_LENGTH> err_msg; |
| |
| // Convert our vector of strings into an array or const char* pointers. |
| std::vector<const char*> argv; |
| argv.reserve(args.size() + 1); |
| for (const std::string& arg : args) { |
| argv.push_back(arg.c_str()); |
| } |
| argv.push_back(nullptr); |
| |
| // Spawn the new job. |
| zx_status_t result = |
| fdio_spawn_etc(job, FDIO_SPAWN_CLONE_ALL, args[0].c_str(), argv.data(), nullptr, 0, nullptr, |
| process_out->reset_and_get_address(), err_msg.data()); |
| if (result != ZX_OK) { |
| *error_message = std::string(err_msg.data()); |
| return result; |
| } |
| |
| return ZX_OK; |
| } |
| |
| zx_status_t ApplyProfileToProcess(const zx::process& process, const zx::profile& profile, |
| bool verbose) { |
| // Find all threads in the given process. |
| constexpr size_t kMaxThreads = 16; |
| std::array<zx_koid_t, kMaxThreads> buffer; |
| size_t num_threads; |
| size_t num_fetched; |
| zx_status_t info_result = process.get_info(ZX_INFO_PROCESS_THREADS, buffer.data(), buffer.size(), |
| &num_threads, &num_fetched); |
| if (info_result != ZX_OK) { |
| return info_result; |
| } |
| if (verbose) { |
| printf("sched: Found %ld thread(s) in child process.\n", num_threads); |
| } |
| |
| // Ensure we found at least 1 thread. |
| if (num_fetched == 0) { |
| return ZX_ERR_BAD_STATE; |
| } |
| |
| // Apply the profile to each one. |
| zx_status_t final_result = ZX_OK; |
| for (size_t i = 0; i < num_fetched; i++) { |
| // Get handle to the thread. |
| zx::thread thread; |
| zx_status_t result = process.get_child(buffer[i], ZX_RIGHT_SAME_RIGHTS, &thread); |
| if (result != ZX_OK) { |
| fprintf(stderr, "sched: Error fetching child thread handle: %s\n", |
| zx_status_get_string(result)); |
| final_result = result; |
| continue; |
| } |
| |
| // Apply the profile. |
| result = thread.set_profile(profile, /*options=*/0); |
| if (result != ZX_OK) { |
| fprintf(stderr, "sched: Could not apply profile to thread: %s\n", |
| zx_status_get_string(result)); |
| final_result = result; |
| continue; |
| } |
| if (verbose) { |
| printf("sched: Successfully applied profile to TID %d\n", thread.get()); |
| } |
| } |
| |
| return final_result; |
| } |
| |
| int Run(int argc, const char** argv) { |
| // Parse arguments. |
| CommandLineArgs args = ParseArgsOrExit(argc, argv); |
| |
| // Create a profile with the given arguments. |
| zx::profile profile; |
| zx_status_t result = CreateProfile(args.priority, "sched", &profile); |
| if (result != ZX_OK) { |
| fprintf(stderr, "Error creating Zircon profile object: %s\n", zx_status_get_string(result)); |
| return 1; |
| } |
| |
| // Launch the given command. |
| std::string error_message; |
| zx::process process; |
| result = Launch(ZX_HANDLE_INVALID, args.params, &process, &error_message); |
| if (result != ZX_OK) { |
| fprintf(stderr, "Could not run command: %s (error %s)\n", error_message.c_str(), |
| zx_status_get_string(result)); |
| return 1; |
| } |
| if (args.verbose) { |
| printf("Launched child process %d\n", process.get()); |
| } |
| |
| // Apply the profile. |
| result = ApplyProfileToProcess(process, profile, args.verbose); |
| if (result != ZX_OK) { |
| fprintf(stderr, "sched: Could not apply profile to threads in process: %s\n", |
| zx_status_get_string(result)); |
| // We continue running the child application anyway. |
| } |
| |
| // Wait for process to finish. |
| process.wait_one(ZX_PROCESS_TERMINATED, zx::time::infinite(), nullptr); |
| if (args.verbose) { |
| printf("Child process terminated.\n"); |
| } |
| |
| return 0; |
| } |
| |
| } // namespace sched |