| #!/bin/bash |
| # 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. |
| |
| #### CATEGORY=Run, inspect and debug |
| ### run fidlcat on given target. |
| |
| ## Runs fidlcat in the given configuration; currently, fidlcat logs all FIDL |
| ## chatter from the given target executable. Starts the debug agent on the |
| ## proposed target, and closes the debug agent on exit. |
| ## |
| ## CAUTION: This support is experimental, and invocation strategy is likely to |
| ## change. The component launching configuration is *especially* likely to go |
| ## away over time. |
| ## |
| ## TROUBLESHOOTING TIPS: |
| ## |
| ## - Remember to use "fx set-device" when working with multiple devices. |
| ## - This scripts by default will mute the SSH connection stdout/stderr, so any |
| ## errors triggered by it won't appear. Use the --debug-mode flag to see |
| ## the debug log's from the debug agent and fidlcat. |
| ## - This scripts uses the tool "nc" for testing TCP connections. Check that it |
| ## is in $PATH and that it works. |
| ## |
| ## Usage: fx fidlcat [--port=<port>] [--with-symbol-server] [--debug-mode] |
| ## [--symbol-path=<path>] [--fidl-ir-path=<path>] [--gdb] |
| ## [--from=<source>] [--to=<path>] [--format=<output>] |
| ## [--with-process-info] [--stack=<value>] |
| ## [--syscalls=<regexp>] [--exclude-syscalls=<regexp>] |
| ## [--messages=<regexp>] [--exclude-messages=<regexp>] [--trigger=<regexp>] |
| ## [--dump-messages] |
| ## [--verbose=<value> | --quiet=<value>] [--log-file <path>] [--stay-alive] |
| ## [--remote-pid=<pid>] [--remote-name=<name>] [--extra-name=<name>] |
| ## [run <component specification>] |
| ## |
| ## System options: |
| ## --port Port the debug agent will be listening on. Will use 2345 by default. |
| ## --with-symbol-server Connect to the symbol server. The first time you use this option, |
| ## fidlcat will give you a link to an authentication page. |
| ## You then have to use the generated key to authenticate. |
| ## --debug-mode Whether the debug agent's debug logs should be shown. |
| ## --symbol-path=<path> An extra location where fidlcat can find debug symbols. |
| ## --fidl-ir-path=<path> An extra location where fidlcat can find FIDL compiled files. |
| ## --gdb Launch fidlcat using gdb. This is only useful to be able to debug |
| ## fidlcat. When this option is used, the string you have to type within |
| ## gdb to launch fidlcat is printed and then, gdb is launched. |
| ## This option only works if you have the unstripped version of fidlcat. |
| ## |
| ## Input options: |
| ## --from=<source> This option must be used at most once. |
| ## Source can be: |
| ## --from=device This is the default input. The input comes from the live |
| ## monitoring of one or several processes. |
| ## At least one of '--remote-pid', '--remote-name', 'run' must |
| ## be specified. |
| ## --from=<path> The input comes from a previously recorded session (protobuf |
| ## format). Path gives the name of the file to read. If path is |
| ## '-' then the standard input is used. |
| ## |
| ## Session save option: |
| ## --to=<path> The session is saved to the specified file (binary protobuf format). |
| ## When a session is saved, you can replay it using "--from=<path>". |
| ## The raw data is saved. That means that the data saved is independent from what is |
| ## displayed. |
| ## |
| ## Format (output) options: |
| ## --format=<output> You can use one of this output formats: |
| ## --format=pretty The session is pretty printed (with colors). |
| ## This is the default output if --with is not used. |
| ## --format=json The session is printed using a json format. |
| ## --format=textproto The session is printed using a text protobuf format. |
| ## --format= Nothing is displayed on the standard output (this option |
| ## only makes sense when used with --to=<path> or with |
| ## --with). |
| ## When there is no output, fidlcat is much faster (this is |
| ## better when you want to monitor real time components). |
| ## This is the default output is --with is used. |
| ## |
| ## Extra generation: |
| ## These options can be used several times. |
| ## --with=summary At the end of the session, a summary of the session is displayed on the |
| ## standard output. |
| ## --with=summary=<path> Like --with=summary but the result is stored into the file specified by |
| ## <path>. |
| ## --with=top At the end of the session, generate a view that groups the output by |
| ## process, protocol, and method. The groups are sorted by number of |
| ## events, so groups with more associated events are listed earlier. |
| ## --with=top=<path> Like --with=top but the result is stored into the file specified by |
| ## <path>. |
| ## |
| ## Display options: |
| ## --with-process-info Display the process name, process id and thread id on |
| ## each line (useful for grep). |
| ## --stack=<value> Define the amount of stack frame to display |
| ## 0: none (default value) |
| ## 1: call site (1 to 4 levels) |
| ## 2: full stack frame (adds some overhead) |
| ## --syscalls=<regexp> A regular expression which selects the syscalls to decode and |
| ## display. |
| ## Can be passed multiple times. |
| ## By default, only zx_channel_.* syscalls are displayed. |
| ## To display all the syscalls, use: --syscalls ".*" |
| ## --exclude-syscalls=<regexp> A regular expression which selects the syscalls to not decode and |
| ## display. |
| ## Can be passed multiple times. |
| ## To be displayed, a syscall must verify --syscalls and not verify |
| ## --exclude-syscalls. |
| ## To display all the syscalls but the zx_handle syscalls, use: |
| ## --syscalls ".*" --exclude-syscalls "zx_handle_.*" |
| ## --messages=<regexp> A regular expression which selects the messages to display. |
| ## To display a message, the method name must satisfy the regexp. |
| ## This option can be specified multiple times. |
| ## Message filtering works on the method's fully qualified name. |
| ## --exclude-messages=<regexp> A regular expression which selects the messages to not display. |
| ## If a message method name satisfy the regexp, the message is not |
| ## displayed (even if it satisfies --messages). |
| ## This option can be specified multiple times. |
| ## Message filtering works on the method's fully qualified name. |
| ## --trigger=<regexp> Start displaying messages and syscalls only when a message for |
| ## which the method name satisfies the filter is found. |
| ## This option can be specified multiple times. |
| ## Message filtering works on the method's fully qualified name. |
| ## --dump-messages Always does a hexadecimal dump of the messages even if we can |
| ## decode them. |
| ## |
| ## Logging options: |
| ## --verbose=<value> The log verbosity. Legal values are "info", "warning", "error", "fatal", |
| ## or a number, starting from 0. Extra verbosity comes with higher levels. |
| ## --quiet=<value> The log verbosity. Legal values are "info", "warning", "error", "fatal", |
| ## or a number, starting from 0. Extra verbosity comes with lower levels. |
| ## --log-file=<path> The log file destination. |
| ## --stay-alive Don't quit fidlcat when all the monitored processes have ended. This allows |
| ## to keep monitoring upcoming process. At the end you have to use control-c |
| ## to quit fidlcat. This is useful when you monitor a process and restart this |
| ## process. |
| ## |
| ## Monitoring options: |
| ## --remote-pid=<pid> The koid of the remote process to trace. |
| ## --remote-name=<name> A set of comma-separated regexes. Fidlcat will monitor all existing |
| ## and future processes whose names match one of the regexes. |
| ## Can be provided multiple times for multiple regexes. |
| ## --extra-name=<name> Like --remote-name, it monitors some processes. However, for these |
| ## processes, monitoring starts only when one of of the "--remote-name" |
| ## process is launched. Also, fidlcat stops when the last "--remote-name" |
| ## process stops (even if some "--extra-name" processes are still |
| ## monitored). |
| ## run <component spec> A token indicating that you want to invoke and trace the following |
| ## component. The component is specified with either a bash regex that |
| ## matches a component URL known to the build, or a full component URL not |
| ## known to your build, but available to your target. |
| ## |
| ## Flags after -- are parsed by fidlcat. |
| ## |
| ## Example usage: |
| ## |
| ## # Attaches to the process with the given pid on the target: |
| ## fx fidlcat --remote-pid=4755 |
| ## |
| ## # Launches the echo client, and monitors its FIDL chatter: |
| ## fx fidlcat run fuchsia-pkg://fuchsia.com/echo_client_cpp#meta/echo_client_cpp.cmx |
| ## |
| ## # Also launches the echo client, and monitors its FIDL chatter: |
| ## fx fidlcat run echo_client_cpp.cmx |
| ## |
| ## # Will trace existing and future processes whose name contains "echo_client" |
| ## fx fidlcat --remote-name=echo_client |
| ## |
| ## All options --remote-pid, --remote-name, --extra-name and, run can be used together. |
| ## However, run must always be the last one. |
| ## When --remote-name and run are used together, only processes which match --remote-name are |
| ## monitored. |
| ## |
| ## Examples (echo_server is launched by echo_client): |
| ## |
| ## # run and monitor echo_client. |
| ## fx fidlcat run echo_client_cpp.cmx |
| ## |
| ## # run and monitor echo_client. |
| ## fx fidlcat --remote-name=echo_client run echo_client_cpp.cmx |
| ## |
| ## # run echo_client and monitor echo_server. |
| ## fx fidlcat --remote-name=echo_server run echo_client_cpp.cmx |
| ## |
| ## # run echo_client and monitor echo_client and echo_server. |
| ## fx fidlcat --remote-name=echo run echo_client_cpp.cmx |
| ## |
| ## # run echo_client and monitor echo_client and echo_server. |
| ## fx fidlcat --remote-name=echo_client --remote-name=echo_server run echo_client_cpp.cmx |
| |
| source "$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)"/../lib/vars.sh || exit $? |
| fx-config-read |
| source "$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)"/lib/debug-agent.sh || exit $? |
| source "$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)"/lib/symbol-index.sh || exit $? |
| |
| launch_agent=1 |
| |
| action= |
| |
| arguments=("--symbol-server=gs://fuchsia-artifacts-release/debug") |
| |
| use_gdb=0 |
| |
| equal_argument() { |
| if [[ $1 != "$2"* ]]; then |
| return 1 |
| fi |
| if [[ $1 != "$2="* ]]; then |
| echo "Flag $2 must be followed by an equal sign: $2=$3" |
| exit 1 |
| fi |
| return 0 |
| } |
| |
| # Options for the zxdb client. |
| agent_out="/dev/null" |
| debug_mode= |
| |
| while [[ $# -gt 0 ]]; do |
| if [[ $1 == "--help" || $1 == "-h" ]]; then |
| fx-command-help |
| exit 0 |
| elif equal_argument $1 "--port" "<port>"; then |
| port=$(echo $1 | cut -c 8-) |
| echo $port |
| elif [[ $1 == "--with-symbol-server" ]]; then |
| echo "--with-symbol-server option is now implicit. You don't need it anymore." |
| elif [[ $1 == "--debug-mode" ]]; then |
| agent_out="/dev/stdout" |
| debug_mode="--debug-mode" |
| elif equal_argument $1 "--symbol-path" "<path>"; then |
| arguments+=("$1") |
| elif equal_argument $1 "--fidl-ir-path" "<path>"; then |
| arguments+=("$1") |
| elif [[ $1 == "--gdb" ]]; then |
| use_gdb=1 |
| elif [[ $1 == "--from=device" ]]; then |
| arguments+=("$1") |
| elif equal_argument $1 "--from" "<source>"; then |
| arguments+=("$1") |
| launch_agent=0 |
| elif equal_argument $1 "--to" "<path>"; then |
| arguments+=("$1") |
| elif [[ $1 == "--format=" ]]; then |
| arguments+=("--format=none") |
| elif equal_argument $1 "--format" "<output>"; then |
| arguments+=("$1") |
| elif equal_argument $1 "--with" "<option>"; then |
| arguments+=("$1") |
| elif [[ $1 == "--with-process-info" ]]; then |
| arguments+=("$1") |
| elif equal_argument $1 "--stack" "<value>"; then |
| arguments+=("$1") |
| elif equal_argument $1 "--syscalls" "<regexp>"; then |
| arguments+=("$1") |
| elif equal_argument $1 "--exclude-syscalls" "<regexp>"; then |
| arguments+=("$1") |
| elif equal_argument $1 "--messages" "<regexp>"; then |
| arguments+=("$1") |
| elif equal_argument $1 "--exclude-messages" "<regexp>"; then |
| arguments+=("$1") |
| elif equal_argument $1 "--trigger" "<regexp>"; then |
| arguments+=("$1") |
| elif [[ $1 == "--dump-messages" ]]; then |
| arguments+=("$1") |
| elif equal_argument $1 "--verbose" "<value>"; then |
| arguments+=("$1") |
| elif equal_argument $1 "--quiet" "<value>"; then |
| arguments+=("$1") |
| elif equal_argument $1 "--log-file" "<path>"; then |
| arguments+=("$1") |
| elif [[ $1 == "--stay-alive" ]]; then |
| arguments+=("$1") |
| elif equal_argument $1 "--remote-pid" "<pid>"; then |
| arguments+=("$1") |
| elif equal_argument $1 "--remote-name" "<name>"; then |
| arguments+=("$1") |
| elif equal_argument $1 "--extra-name" "<name>"; then |
| arguments+=("$1") |
| elif [[ $1 == "run" ]]; then |
| action="$1" |
| shift |
| break # Remaining flags are passed to fidlcat, with processing below |
| elif [[ $1 == "--" ]]; then |
| shift |
| break # Remaining flags are passed to fidlcat |
| else |
| echo "Invalid flag $1" |
| exit 1 |
| fi |
| shift |
| done |
| |
| # Infer the package URL from a regex-specified name. |
| package_file="${FUCHSIA_BUILD_DIR}/component_index_metadata" |
| if [[ "${action}" == "run" ]]; then |
| component=$1 |
| components=() |
| all_pkgs="$(cat "${package_file}")" |
| if [[ -n "${all_pkgs}" ]]; then |
| for pkg in ${all_pkgs}; do |
| if [[ "${pkg}" =~ ${component} ]]; then |
| components+=("${pkg}") |
| fi |
| done |
| else |
| # If there aren't any packages in the component_index_metadata file, then |
| # assume the users know what they are doing. fidlcat should complain. |
| components+=("${component}") |
| fi |
| |
| if [[ ${#components[@]} = 0 ]]; then |
| fx-error "Package $component is not known to the current build configuration." |
| fx-error "Check \`fx list-packages\` for the correct name," |
| fx-error "or adjust the build configuration with \`fx set\`." |
| exit 1 |
| elif [[ ${#components[@]} -gt 1 ]]; then |
| fx-error "Ambiguous match: $component matches the following ${#components[@]} packages:" |
| for component in "${components[@]}"; do |
| fx-error " $component" |
| done |
| exit 1 |
| fi |
| component="${components[0]}" |
| shift |
| # We have now removed "run <partial component URL regex>" from the remaining |
| # positional parameters. The following set command replaces them with "run |
| # <full component URL>". |
| set - "$@" "run" "${component}" |
| fi |
| |
| if [[ -z "${port}" ]]; then |
| port=2345 |
| fi |
| |
| ensure-symbol-index-registered || echo "Failed to register ${FUCHSIA_DIR} in symbol-index!" |
| |
| if [[ ${launch_agent} -eq 1 ]]; then |
| if launch_debug_agent "${port}" "" "${agent_out}"; then |
| arguments+=("--connect" "$(get-device-addr-resource):${port}") |
| arguments+=("--fidl-ir-path" @"${FUCHSIA_BUILD_DIR}"/all_fidl_json.txt) |
| arguments+=("--quit-agent-on-exit") |
| arguments+=("$@") |
| |
| if [[ ${use_gdb} -eq 1 ]]; then |
| # Starts gdb. |
| gdb --args ${FUCHSIA_BUILD_DIR}/host_x64/exe.unstripped/fidlcat ${arguments[@]} |
| else |
| # Now that the debug agent is launched, starts fidlcat. |
| "${FUCHSIA_BUILD_DIR}/host-tools/fidlcat" ${arguments[@]} |
| fi |
| |
| # --quit-agent-on-exit should quit the debug_agent so we just need to wait for ssh to terminate. |
| wait |
| else |
| fx-error "Could not launch debug agent. Exiting. Make sure you're running 'fx serve'." |
| exit 1 |
| fi |
| else |
| arguments+=("--fidl-ir-path" @"${FUCHSIA_BUILD_DIR}"/all_fidl_json.txt) |
| arguments+=("$@") |
| |
| if [[ ${use_gdb} -eq 1 ]]; then |
| # Starts gdb. |
| gdb --args ${FUCHSIA_BUILD_DIR}/host_x64/exe.unstripped/fidlcat ${arguments[@]} |
| else |
| # Now that the debug agent is launched, starts fidlcat. |
| "${FUCHSIA_BUILD_DIR}/host-tools/fidlcat" ${arguments[@]} |
| fi |
| fi |