| #!/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>] | |
| ## [--remote-job-id=<koid>] [--remote-job-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. 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', | |
| ## '--remote-job-id', --'remote-job-name', 'run' must be | |
| ## specified. | |
| ## --from=dump The input comes from stdin which is the log output of one | |
| ## or several programs. The lines in the log which dump | |
| ## syscalls are decoded and replaced by the decoded version. | |
| ## All other lines are unchanged. | |
| ## --from=<path> Playback. Used to replay a session previously recorded with | |
| ## --to=<path> (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>. | |
| ## --with=group-by-thread For each thread display a short version of all the events. | |
| ## --with=group-by-thread=<path> Like --with=group-by-thread 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. | |
| ## --thread=<thread koid> Only display the events for the specified thread. | |
| ## This option can be specified multiple times to display several | |
| ## threads. By default all the events are displayed. | |
| ## --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> The name of a process. Fidlcat will monitor all existing and future | |
| ## processes whose names includes <name> (<name> is a substring of the | |
| ## process name). | |
| ## Can be provided multiple times for multiple names. | |
| ## When used with --remote-job-id or --remote-job-name, only the processes | |
| ## from the selected jobs are taken into account. | |
| ## --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). | |
| ## | |
| ## --remote-job-id=<koid> The koid of a remote job for which we want to monitor all the | |
| ## processes. | |
| ## Can be provided multiple times for multiple jobs. | |
| ## Only jobs created before fidlcat is launched are monitored. | |
| ## --remote-job-name=<name> The name of a remote job for which we want to monitor all the | |
| ## processes. All the jobs which contain <name> in their name are used. | |
| ## Can be provided multiple times for multiple jobs. | |
| ## Only jobs created before fidlcat is launched are 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" | |
| 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" | |
| 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 [[ $1 == "--from=dump" ]]; then | |
| arguments+=("$1") | |
| launch_agent=0 | |
| 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 equal_argument "$1" "--thread" "<thread koid>"; 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 equal_argument "$1" "--remote-job-id" "<koid>"; then | |
| arguments+=("$1") | |
| elif equal_argument "$1" "--remote-job-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!" | |
| FIDLCAT=$(fx-command-run host-tool --print fidlcat) | |
| FIDLCAT_UNSTRIPPED="$(dirname "$FIDLCAT")/exe.unstripped/$(basename "$FIDLCAT")" | |
| 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 "$FIDLCAT_UNSTRIPPED" "${arguments[@]}" | |
| else | |
| # Now that the debug agent is launched, start fidlcat. | |
| "$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 "$FIDLCAT_UNSTRIPPED" "${arguments[@]}" | |
| else | |
| # Now that the debug agent is launched, start fidlcat. | |
| "$FIDLCAT" "${arguments[@]}" | |
| fi | |
| fi |