bind/debugger/
debug_dump.rs

1// Copyright 2022 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use crate::compiler::symbol_table::{get_deprecated_key_identifiers, Symbol};
6use crate::interpreter::common::BytecodeError;
7use crate::interpreter::decode_bind_rules::{DecodedCompositeBindRules, DecodedRules, Node};
8use crate::interpreter::instruction_decoder::{DecodedCondition, DecodedInstruction};
9use crate::parser::common::NodeType;
10
11fn dump_node(
12    node: &Node,
13    node_type: NodeType,
14    decoded_rules: &DecodedCompositeBindRules,
15) -> String {
16    let node_name = decoded_rules
17        .symbol_table
18        .get(&node.name_id)
19        .map_or_else(|| "N/A".to_string(), |name| name.clone());
20    let mut node_dump = match node_type {
21        NodeType::Primary => format!("Node (primary): {}", node_name),
22        NodeType::Additional => format!("Node: {}", node_name),
23        NodeType::Optional => format!("Node (optional): {}", node_name),
24    };
25    node_dump.push_str(&dump_instructions(node.decoded_instructions.clone()));
26    node_dump.push_str("\n");
27    node_dump
28}
29
30pub fn dump_bind_rules(bytecode: Vec<u8>) -> Result<String, BytecodeError> {
31    match DecodedRules::new(bytecode)? {
32        DecodedRules::Normal(decoded_rules) => {
33            Ok(dump_instructions(decoded_rules.decoded_instructions))
34        }
35        DecodedRules::Composite(rules) => {
36            let mut node_dump = dump_node(&rules.primary_node, NodeType::Primary, &rules);
37
38            if !rules.additional_nodes.is_empty() {
39                let additional_nodes_dump = rules
40                    .additional_nodes
41                    .iter()
42                    .map(|node| dump_node(node, NodeType::Additional, &rules))
43                    .collect::<Vec<String>>()
44                    .concat();
45                node_dump.push_str(&additional_nodes_dump);
46            }
47
48            if !rules.optional_nodes.is_empty() {
49                let optional_nodes_dump = rules
50                    .optional_nodes
51                    .iter()
52                    .map(|node| dump_node(&node, NodeType::Optional, &rules))
53                    .collect::<Vec<String>>()
54                    .concat();
55                node_dump.push_str(&optional_nodes_dump);
56            }
57            Ok(node_dump)
58        }
59    }
60}
61
62fn dump_condition(cond: DecodedCondition) -> String {
63    let op = if cond.is_equal { "==" } else { "!=" };
64    let lhs_dump = match cond.lhs {
65        Symbol::NumberValue(value) => {
66            let deprecated_keys = get_deprecated_key_identifiers();
67            match deprecated_keys.get(&(value as u32)) {
68                Some(value) => value.clone(),
69                None => value.to_string(),
70            }
71        }
72        _ => cond.lhs.to_string(),
73    };
74    format!("{} {} {}", lhs_dump, op, cond.rhs)
75}
76
77// TODO(https://fxbug.dev/42175142): Print the label IDs in the jump and label statements.
78fn dump_instructions(instructions: Vec<DecodedInstruction>) -> String {
79    let mut bind_rules_dump = String::new();
80    for inst in instructions {
81        let inst_dump = match inst {
82            DecodedInstruction::UnconditionalAbort => "  Abort".to_string(),
83            DecodedInstruction::Condition(cond) => format!("  {}", dump_condition(cond)),
84            DecodedInstruction::Jump(cond) => match cond {
85                Some(condition) => format!("  Jump if {} to ??", dump_condition(condition)),
86                None => "  Jump to ??".to_string(),
87            },
88            DecodedInstruction::Label => "  Label ??".to_string(),
89        };
90
91        bind_rules_dump.push_str("\n");
92        bind_rules_dump.push_str(&inst_dump);
93    }
94
95    bind_rules_dump
96}
97
98#[cfg(test)]
99mod test {
100    use super::*;
101    use crate::bytecode_constants::*;
102
103    const BIND_HEADER: [u8; 8] = [0x42, 0x49, 0x4E, 0x44, 0x02, 0, 0, 0];
104
105    fn append_section_header(bytecode: &mut Vec<u8>, magic_num: u32, sz: u32) {
106        bytecode.extend_from_slice(&magic_num.to_be_bytes());
107        bytecode.extend_from_slice(&sz.to_le_bytes());
108    }
109
110    #[test]
111    fn test_bytecode_print() {
112        let mut bytecode: Vec<u8> = BIND_HEADER.to_vec();
113        bytecode.push(BYTECODE_DISABLE_DEBUG);
114        append_section_header(&mut bytecode, SYMB_MAGIC_NUM, 18);
115
116        let str_1: [u8; 5] = [0x57, 0x52, 0x45, 0x4E, 0]; // "WREN"
117        bytecode.extend_from_slice(&[1, 0, 0, 0]);
118        bytecode.extend_from_slice(&str_1);
119
120        let str_2: [u8; 5] = [0x44, 0x55, 0x43, 0x4B, 0]; // "DUCK"
121        bytecode.extend_from_slice(&[2, 0, 0, 0]);
122        bytecode.extend_from_slice(&str_2);
123
124        append_section_header(&mut bytecode, INSTRUCTION_MAGIC_NUM, 28);
125
126        let instructions = [
127            0x01, 0x01, 0, 0, 0, 0x05, 0x01, 0x10, 0, 0, 0x10, // 0x05000000 == 0x10000010
128            0x11, 0x01, 0, 0, 0, 0x00, 0x01, 0, 0, 0, 0x02, // jmp 1 if key("WREN") == "DUCK"
129            0x02, 0, 0, 0,    // jmp 1 if key("WREN") == "DUCK"
130            0x30, // abort
131            0x20, // jump pad
132        ];
133        bytecode.extend_from_slice(&instructions);
134
135        let expected_dump =
136            "\n  83886080 == 268435472\n  Jump if Key(WREN) == \"DUCK\" to ??\n  Abort\n  Label ??";
137        assert_eq!(expected_dump.to_string(), dump_bind_rules(bytecode).unwrap());
138    }
139
140    #[test]
141    fn test_composite_bytecode_print() {
142        let bytecode = vec![
143            0x42, 0x49, 0x4e, 0x44, 0x02, 0x00, 0x00, 0x00, // BIND header
144            0x00, // debug_flag byte
145            0x53, 0x59, 0x4e, 0x42, 0x28, 0x00, 0x00, 0x00, // SYMB header
146            0x01, 0x00, 0x00, 0x00, // "wallcreeper" ID
147            0x77, 0x61, 0x6c, 0x6c, 0x63, 0x72, 0x65, 0x65, // "wallcree"
148            0x70, 0x65, 0x72, 0x00, // "per"
149            0x02, 0x00, 0x00, 0x00, // "wagtail" ID
150            0x77, 0x61, 0x67, 0x74, 0x61, 0x69, 0x6c, 0x00, // "wagtail"
151            0x03, 0x00, 0x00, 0x00, // "redpoll" ID
152            0x72, 0x65, 0x64, 0x70, 0x6f, 0x6c, 0x6c, 0x00, // "redpoll"
153            0x43, 0x4f, 0x4d, 0x50, 0x2d, 0x00, 0x00, 0x00, // COMP header
154            0x01, 0x00, 0x00, 0x00, // Device name ID
155            0x50, 0x02, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, // Primary node header
156            0x01, 0x01, 0x01, 0x00, 0x00, 0x00, // BIND_PROTOCOL ==
157            0x01, 0x01, 0x00, 0x00, 0x00, // 1
158            0x51, 0x03, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, // Node header
159            0x02, 0x01, 0x01, 0x00, 0x00, 0x00, // BIND_PROTOCOL !=
160            0x01, 0x02, 0x00, 0x00, 0x00, // 2
161            0x30, // abort
162        ];
163
164        let expected_dump = "Node (primary): wagtail\n  \
165            fuchsia.BIND_PROTOCOL == 1\n\
166            Node: redpoll\n  \
167            fuchsia.BIND_PROTOCOL != 2\n  \
168            Abort\n";
169        assert_eq!(expected_dump.to_string(), dump_bind_rules(bytecode).unwrap());
170    }
171}