| // Copyright 2020 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 "src/developer/debug/zxdb/console/commands/verb_attach_job.h" |
| |
| #include "src/developer/debug/zxdb/client/filter.h" |
| #include "src/developer/debug/zxdb/client/job.h" |
| #include "src/developer/debug/zxdb/client/session.h" |
| #include "src/developer/debug/zxdb/client/system.h" |
| #include "src/developer/debug/zxdb/console/command.h" |
| #include "src/developer/debug/zxdb/console/command_utils.h" |
| #include "src/developer/debug/zxdb/console/console.h" |
| #include "src/developer/debug/zxdb/console/format_filter.h" |
| #include "src/developer/debug/zxdb/console/output_buffer.h" |
| #include "src/developer/debug/zxdb/console/verbs.h" |
| #include "src/lib/fxl/strings/string_printf.h" |
| |
| namespace zxdb { |
| |
| namespace { |
| |
| constexpr int kAttachSystemRootSwitch = 1; |
| |
| const char kAttachJobShortHelp[] = "attach-job / aj: Watch for process launches in a job."; |
| const char kAttachJobHelp[] = |
| R"(attach-job <job-koid> [ <filter> ]* |
| |
| Alias: aj |
| |
| A job is a node in the Zircon process tree that contains processes and other |
| jobs. Attaching to a job allows watching for process launches in that job and |
| all of its sub-jobs. |
| |
| • See the current system's job/process tree with the "ps" command. |
| |
| The debugger maintains a list of "job contexts" which are numbered starting |
| from one. Each can be attached to a Zircon job or not. When referring to a job |
| object in the debugger, use the index of the job context. |
| |
| • See the current job contexts with the "job" command. |
| • Detach a context from a job using "job X detach" where X is the index of |
| the job context from the "job" list. |
| |
| Watching processes in a job |
| |
| Filters apply to the processes in attached jobs to determine which new |
| processes to attach to. A filter can apply to one specific job or to all |
| attached jobs. Without filters, attach-job will just attach to the job but |
| will not actually attach to any processes. |
| |
| • See the currently filters with the "filter" command. |
| • Create a new filter either by adding it as a 2nd argument to "attach-job" |
| or later using "attach". |
| • Attach to all processes in a job with "attach-job <koid> *". Note that * |
| is a special string for filters, regular expressions are not supported. |
| • See "help attach" for more on creating filters. |
| |
| Arguments |
| |
| -r |
| --root |
| Attaches to the system's root job. No job koid is read. |
| |
| More about jobs |
| |
| On startup the debugger will attempt to attach to the system root job. This |
| allows filters to apply to all processes in the system without having to |
| attach separately to a specific job. |
| |
| Each job and process can have only one attached debugger system-wide. New |
| process notifications are delivered to the most specific attached job only. |
| Permissions can also affect whether the debugger has the ability to see the |
| root job so you may find the root job is not attached. |
| |
| • Using job filters with multiple debuggers is not advised unless watching |
| completely non-overlapping jobs. |
| |
| • Even within the same debugger, if there are multiple overapping job |
| contexts only the most specific one's filters will apply to a launched |
| process. |
| |
| Examples |
| |
| attach-job 12345 |
| Attaches to the job with koid 12345. Existing filters (if any) will apply. |
| |
| attach-job 12345 myprocess |
| Attaches to job 12345 and filters for processes inside it with "myprocess" |
| in the name. |
| |
| attach job 12345 |
| job 2 attach myprocess // Assuming the previous command made job context #2. |
| Same as the above example but the attach is done with a separate command. |
| |
| aj -r |
| Attaches to the system root job. |
| |
| job 2 detach |
| job 2 attach-job 5678 |
| Detaches the job context #2 from the job it was attached to and then |
| attaches it to job 5678. |
| )"; |
| |
| bool IsJobAttachable(Job* job) { return job->state() == Job::State::kNone; } |
| |
| // Searches for an existing attached job weith the given koid. |
| Job* GetJobAlreadyAttached(System* system, uint64_t job_koid) { |
| for (Job* job : system->GetJobs()) { |
| if (job->state() == Job::State::kAttached && job->koid() == job_koid) |
| return job; |
| } |
| return nullptr; |
| } |
| |
| Err RunVerbAttachJob(ConsoleContext* context, const Command& cmd, CommandCallback callback) { |
| if (Err err = cmd.ValidateNouns({Noun::kJob}); err.has_error()) |
| return err; |
| |
| // Index of the first argument that's a filter. |
| size_t first_filter_index = 0; |
| |
| // Which job to attach to. |
| enum AttachToWhat { kAttachSystemRoot, kAttachKoid } attach_to_what; |
| uint64_t attach_koid = 0; // Valid when attach_to_what == kAttachKoid. |
| |
| if (cmd.HasSwitch(kAttachSystemRootSwitch)) { |
| attach_to_what = kAttachSystemRoot; |
| } else { |
| // Attach by koid. |
| if (Err err = ReadUint64Arg(cmd, 0, "job koid", &attach_koid); err.has_error()) |
| return err; |
| attach_to_what = kAttachKoid; |
| first_filter_index = 1; // Filters start after the job koid. |
| } |
| |
| // Figure out which job to attach. |
| Job* job = nullptr; |
| if (int job_index = cmd.GetNounIndex(Noun::kJob); job_index != Command::kNoIndex) { |
| // User gave an explicit job to attach, it must be attachable. |
| if (!IsJobAttachable(cmd.job())) |
| return Err("The requested job is already attached."); |
| job = cmd.job(); |
| } else if (attach_to_what == kAttachKoid && |
| (job = GetJobAlreadyAttached(&context->session()->system(), attach_koid))) { |
| // The debugger is already attached to the requested koid, re-use it. |
| } else if (cmd.job() && IsJobAttachable(cmd.job())) { |
| // Use the current job. |
| job = cmd.job(); |
| } else { |
| // Create a new job and set it as the current one. |
| job = context->session()->system().CreateNewJob(); |
| context->SetActiveJob(job); |
| } |
| |
| auto cb = [callback = std::move(callback)](fxl::WeakPtr<Job> job, const Err& err) mutable { |
| JobCommandCallback("attach-job", job, true, err, std::move(callback)); |
| }; |
| |
| switch (attach_to_what) { |
| case kAttachSystemRoot: |
| job->AttachToSystemRoot(std::move(cb)); |
| break; |
| case kAttachKoid: |
| // Only attach if it's not already attached. It will be attached already if an existing job |
| // attachment was found with the requested koid. |
| if (job->state() == Job::State::kNone) |
| job->Attach(attach_koid, std::move(cb)); |
| break; |
| } |
| |
| // Create filters attached to this job if requested. |
| for (size_t i = first_filter_index; i < cmd.args().size(); i++) { |
| Filter* filter = context->session()->system().CreateNewFilter(); |
| filter->SetJob(job); |
| filter->SetPattern(cmd.args()[i]); |
| |
| context->SetActiveFilter(filter); |
| |
| // Output a record of the created filter. |
| OutputBuffer out("Created "); |
| out.Append(FormatFilter(context, filter)); |
| Console::get()->Output(out); |
| } |
| |
| return Err(); |
| } |
| |
| } // namespace |
| |
| VerbRecord GetAttachJobVerbRecord() { |
| VerbRecord attach_job(&RunVerbAttachJob, {"attach-job", "aj"}, kAttachJobShortHelp, |
| kAttachJobHelp, CommandGroup::kProcess); |
| attach_job.switches.push_back(SwitchRecord(kAttachSystemRootSwitch, false, "root", 'r')); |
| return attach_job; |
| } |
| |
| } // namespace zxdb |