| // Copyright 2016 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 "thread_action_list.h" |
| |
| #include "garnet/lib/debugger_utils/util.h" |
| |
| #include "lib/fxl/logging.h" |
| |
| #include "util.h" |
| |
| // N.B. This file is included in the unittest, which does not |
| // contain all the inferior-control logic. Therefore do not |
| // #include server.h. |
| |
| namespace debugserver { |
| |
| ThreadActionList::Entry::Entry(ThreadActionList::Action action, zx_koid_t pid, |
| zx_koid_t tid) |
| : action_(action), pid_(pid), tid_(tid) { |
| FXL_DCHECK(pid_ != 0); |
| // A tid value of zero is ok. |
| } |
| |
| void ThreadActionList::Entry::set_picked_tid(zx_koid_t tid) { |
| FXL_DCHECK(tid != 0); |
| FXL_DCHECK(tid_ == 0); |
| tid_ = tid; |
| } |
| |
| bool ThreadActionList::Entry::Contains(zx_koid_t pid, zx_koid_t tid) const { |
| FXL_DCHECK(pid != 0 && pid != kAll); |
| FXL_DCHECK(tid != 0 && tid != kAll); |
| // A "0" meaning "arbitrary process" is resolved to the current process at |
| // construction time. A "0" meaning "arbitrary thread" must be resolved by |
| // the caller. If it cannot be resolved it is left as zero, and there is |
| // no match. |
| FXL_DCHECK(pid_ != 0); |
| if (pid != pid_ && pid_ != kAll) |
| return false; |
| if (tid != tid_ && tid_ != kAll) |
| return false; |
| return true; |
| } |
| |
| bool ThreadActionList::DecodeAction(char c, |
| ThreadActionList::Action* out_action) { |
| switch (c) { |
| case 'c': |
| *out_action = Action::kContinue; |
| break; |
| case 's': |
| *out_action = Action::kStep; |
| break; |
| default: |
| return false; |
| } |
| |
| return true; |
| } |
| |
| const char* ThreadActionList::ActionToString(ThreadActionList::Action action) { |
| #define CASE_TO_STR(x) \ |
| case x: \ |
| return #x |
| switch (action) { |
| CASE_TO_STR(Action::kNone); |
| CASE_TO_STR(Action::kContinue); |
| CASE_TO_STR(Action::kStep); |
| default: |
| break; |
| } |
| #undef CASE_TO_STR |
| return "(unknown)"; |
| } |
| |
| ThreadActionList::ThreadActionList(const fxl::StringView& str, |
| zx_koid_t cur_proc) { |
| size_t len = str.size(); |
| size_t s = 0; |
| Action default_action = Action::kNone; |
| |
| if (len == 0) { |
| FXL_LOG(ERROR) << "Empty action string"; |
| return; |
| } |
| |
| while (s < len) { |
| size_t semi = str.find(';', s); |
| size_t n; |
| if (semi == str.npos) |
| n = len - s; |
| else |
| n = semi - s; |
| if (n == 0) { |
| FXL_LOG(ERROR) << "Missing action: " << str; |
| return; |
| } |
| Action action; |
| if (!DecodeAction(str[s], &action)) { |
| FXL_LOG(ERROR) << "Bad action: " << str; |
| return; |
| } |
| if (n == 1) { |
| if (default_action != Action::kNone) { |
| FXL_LOG(ERROR) << "Multiple default actions: " << str; |
| return; |
| } |
| default_action = action; |
| } else if (str[s + 1] == ':') { |
| bool has_pid; |
| // TODO(dje): koids are uint64_t |
| int64_t pid, tid; |
| if (!ParseThreadId(str.substr(s + 2, n - 2), &has_pid, &pid, &tid)) { |
| FXL_LOG(ERROR) << "Bad thread id in action: " << str; |
| return; |
| } |
| if ((has_pid && pid <= -2) || tid <= -2) { |
| FXL_LOG(ERROR) << "Bad thread id in action: " << str; |
| return; |
| } |
| if (!has_pid || pid == 0) |
| pid = cur_proc; |
| if (pid == -1 && tid != -1) { |
| FXL_LOG(ERROR) << "All processes and one thread: " << str; |
| return; |
| } |
| actions_.push_back( |
| Entry(action, pid == -1 ? kAll : pid, tid == -1 ? kAll : tid)); |
| } else { |
| FXL_LOG(ERROR) << "Syntax error in action: " << str; |
| return; |
| } |
| if (semi == str.npos) |
| s = len; |
| else |
| s = semi + 1; |
| } |
| |
| default_action_ = default_action; |
| valid_ = true; |
| } |
| |
| ThreadActionList::Action ThreadActionList::GetAction(zx_koid_t pid, |
| zx_koid_t tid) const { |
| FXL_DCHECK(pick_ones_resolved_); |
| |
| for (auto e : actions_) { |
| if (e.Contains(pid, tid)) |
| return e.action(); |
| } |
| |
| return default_action_; |
| } |
| |
| } // namespace debugserver |