wasm-gc-breakpoints.js 8.31 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
// Copyright 2021 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// Flags: --experimental-wasm-gc

utils.load('test/inspector/wasm-inspector-test.js');

const {session, contextGroup, Protocol} =
    InspectorTest.start('Tests GC object inspection.');
session.setupScriptMap();

const module_bytes = [
  0x00, 0x61, 0x73, 0x6d, 1, 0, 0, 0,  // wasm magic

  0x01,  // type section
17 18
  0x18,  // section length
  0x01,  // number of type section entries
19
  0x4f,  // recursive type group
20
  0x04,  // number of types in the recursive group
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
  // type 0: struct $StrA (field ($byte i8) ($word i16) ($pointer (ref $StrB)))
  0x5f,  // struct
  0x03,  // field count
  0x7a, 0x01,  // mut i8
  0x79, 0x00,  // i16
  0x6b, 0x01, 0x01,  // mut ref $StrB
  // type 1: struct $StrB (field ($next (ref null $StrA)))
  0x5f,  // struct
  0x01,  // field count
  0x6c, 0x00, 0x01,  // mut ref null $StrA
  // type 2: array $ArrC (mut (ref null $StrA))
  0x5e,  // array
  0x6c, 0x00, 0x01,  // mut ref null $StrA
  // type 3: func
  0x60,  // signature
  0x00,  // number of params
  0x00,  // number of results

  0x03,  // function section
  0x02,  // section length
  0x01,  // number of functions
  0x03,  // function 0: signature 3

  // This is just so that function index 0 counts as declared.
  0x06,  // global section
  0x07,  // section length
  0x01,  // number of globals
  0x6c, 0x03,  // type of global: ref null $sig3
  0x00,  // immutable
  0xd2, 0x00, 0x0b,  // initializer: ref.func $func1; end

  0x07,  // export section
  0x08,  // section length
  0x01,  // number of exports
  0x04,  // length of "main"
  0x6d, 0x61, 0x69, 0x6e,  // "main"
  0x00,  // kind: function
  0x00,  // index: 0

  /////////////////////////// CODE SECTION //////////////////////////
  0x0a,  // code section
  0x35,  // section length
  0x01,  // number of functions

  0x33,  // function 0: size
  0x02,  // number of locals
  0x01, 0x6c, 0x00,  // (local $varA (ref null $StrA))
  0x01, 0x6c, 0x02,  // (local $varC (ref null $ArrC))
  // $varA := new $StrA(127, 32767, new $StrB(null))
  0x41, 0xFF, 0x00,  // i32.const 127
  0x41, 0xFF, 0xFF, 0x01,  // i32.const 32767
  0xfb, 0x30, 0x01,  // rtt.canon $StrB
  0xfb, 0x02, 0x01,  // struct.new_default_with_rtt $StrB
  0xfb, 0x30, 0x00,  // rtt.canon $StrA
  0xfb, 0x01, 0x00,  // struct.new_with_rtt $StrA
  0x22, 0x00,  // local.tee $varA
  // $varA.$pointer.$next = $varA
  0xfb, 0x03, 0x00, 0x02,  // struct.get $StrA $pointer
  0x20, 0x00,  // local.get $varA
  0xfb, 0x06, 0x01, 0x00,  // struct.set $StrB $next
  // $varC := new $ArrC($varA)
  0x20, 0x00,  // local.get $varA -- value
  0x41, 0x01,  // i32.const 1 -- length
  0xfb, 0x30, 0x02,  // rtt.canon $ArrC
  0xfb, 0x11, 0x02,  // array.new_with_rtt $ArrC
  0x21, 0x01,  // local.set $varC
  0x0b,  // end

  /////////////////////////// NAME SECTION //////////////////////////
  0x00,  // name section
91
  0xd4, 0x01, // section length
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
  0x04,  // length of "name"
  0x6e, 0x61, 0x6d, 0x65,  // "name"

  0x02,  // "local names" subsection
  0x0f,  // length of subsection
  0x01,  // number of entries
  0x00,  // for function 0
  0x02,  // number of entries for function 0
  0x00,  // local index
  0x04,  // length of "varA"
  0x76, 0x61, 0x72, 0x41,  // "varA"
  0x01,  // local index
  0x04,  // length of "varB"
  0x76, 0x61, 0x72, 0x42,  // "varB"

  0x04,  // "type names" subsection
108
  0x99, 0x01,  // length of subsection
109 110 111 112 113
  0x03,  // number of entries
  0x00,  // type index
  0x04,  // name length
  0x53, 0x74, 0x72, 0x41,  // "StrA"
  0x01,  // type index
114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
  0x89, 0x01,  // name length
  // Called "$StrB" in other comments, actual name:
  // "veryLongNameWithMoreThanOneHundredAndTwentyEightCharactersToTestThat
  // WeAreHandlingStringBufferOverflowWithoutCrashing_ThisWontGetTruncated"
  0x76, 0x65, 0x72, 0x79, 0x4c, 0x6f, 0x6e, 0x67, 0x4e, 0x61, 0x6d, 0x65, 0x57,
  0x69, 0x74, 0x68, 0x4d, 0x6f, 0x72, 0x65, 0x54, 0x68, 0x61, 0x6e, 0x4f, 0x6e,
  0x65, 0x48, 0x75, 0x6e, 0x64, 0x72, 0x65, 0x64, 0x41, 0x6e, 0x64, 0x54, 0x77,
  0x65, 0x6e, 0x74, 0x79, 0x45, 0x69, 0x67, 0x68, 0x74, 0x43, 0x68, 0x61, 0x72,
  0x61, 0x63, 0x74, 0x65, 0x72, 0x73, 0x54, 0x6f, 0x54, 0x65, 0x73, 0x74, 0x54,
  0x68, 0x61, 0x74, 0x57, 0x65, 0x41, 0x72, 0x65, 0x48, 0x61, 0x6e, 0x64, 0x6c,
  0x69, 0x6e, 0x67, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x42, 0x75, 0x66, 0x66,
  0x65, 0x72, 0x4f, 0x76, 0x65, 0x72, 0x66, 0x6c, 0x6f, 0x77, 0x57, 0x69, 0x74,
  0x68, 0x6f, 0x75, 0x74, 0x43, 0x72, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, 0x5f,
  0x54, 0x68, 0x69, 0x73, 0x57, 0x6f, 0x6e, 0x74, 0x47, 0x65, 0x74, 0x54, 0x72,
  0x75, 0x6e, 0x63, 0x61, 0x74, 0x65, 0x64,
129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
  0x02,  // type index
  0x04,  // name length
  0x41, 0x72, 0x72, 0x43,  // "ArrC"

  0x0a,  // "field names" subsection
  0x20,  // length of subsection
  0x02,  // number of types
  0x00,  // for type $StrA
  0x03,  // number of entries for $StrA
  0x00,  // field index 0
  0x04,  // length of "byte"
  0x62, 0x79, 0x74, 0x65,  // "byte"
  0x01,  // field index 1
  0x04,  // length of "word"
  0x77, 0x6f, 0x72, 0x64,  // "word"
  0x02,  // field index 2
  0x07,  // length of "pointer"
  0x70, 0x6f, 0x69, 0x6e, 0x74, 0x65, 0x72,  // "pointer"
  0x01,  // for type $StrB
  0x01,  // number of entries for $StrB
  0x00,  // field index
  0x04,  // length of "next"
  0x6e, 0x65, 0x78, 0x74,  // "next"
];

const getResult = msg => msg.result || InspectorTest.logMessage(msg);

function setBreakpoint(offset, scriptId, scriptUrl) {
  InspectorTest.log(
      'Setting breakpoint at offset ' + offset + ' on script ' + scriptUrl);
  return Protocol.Debugger
      .setBreakpoint({
        'location':
            {'scriptId': scriptId, 'lineNumber': 0, 'columnNumber': offset}
      })
      .then(getResult);
}

Protocol.Debugger.onPaused(async msg => {
  let loc = msg.params.callFrames[0].location;
  InspectorTest.log('Paused:');
  await session.logSourceLocation(loc);
  InspectorTest.log('Scope:');
  for (var frame of msg.params.callFrames) {
    var functionName = frame.functionName || '(anonymous)';
    var lineNumber = frame.location.lineNumber;
    var columnNumber = frame.location.columnNumber;
    InspectorTest.log(`at ${functionName} (${lineNumber}:${columnNumber}):`);
177
    if (!/^wasm/.test(session.getCallFrameUrl(frame))) {
178 179 180 181 182
      InspectorTest.log('   -- skipped');
      continue;
    }
    for (var scope of frame.scopeChain) {
      InspectorTest.logObject(' - scope (' + scope.type + '):');
183 184 185 186 187 188 189 190 191
      var { objectId } = scope.object;
      if (scope.type == 'wasm-expression-stack') {
        objectId = (await Protocol.Runtime.callFunctionOn({
          functionDeclaration: 'function() { return this.stack }',
          objectId
        })).result.result.objectId;
      }
      var properties =
          await Protocol.Runtime.getProperties({objectId});
192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213
      await WasmInspectorTest.dumpScopeProperties(properties);
      if (scope.type === 'wasm-expression-stack' || scope.type === 'local') {
        for (var value of properties.result.result) {
          var details = await Protocol.Runtime.getProperties(
              {objectId: value.value.objectId});
          var nested_value =
              details.result.result.find(({name}) => name === 'value');
          if (!nested_value.value.objectId) continue;
          details = await Protocol.Runtime.getProperties(
              {objectId: nested_value.value.objectId});
          InspectorTest.log('     object details:');
          await WasmInspectorTest.dumpScopeProperties(details);
        }
      }
    }
  }

  Protocol.Debugger.resume();
});

InspectorTest.runAsyncTestSuite([
  async function test() {
214
    await Protocol.Runtime.enable();
215 216 217 218 219 220 221 222 223
    await Protocol.Debugger.enable();
    InspectorTest.log('Instantiating.');
    // Spawn asynchronously:
    WasmInspectorTest.instantiate(module_bytes);
    InspectorTest.log(
        'Waiting for wasm script (ignoring first non-wasm script).');
    // Ignore javascript and full module wasm script, get scripts for functions.
    const [, {params: wasm_script}] =
        await Protocol.Debugger.onceScriptParsed(2);
224
    let offset = 109;  // "local.set $varC" at the end.
225 226 227 228 229 230
    await setBreakpoint(offset, wasm_script.scriptId, wasm_script.url);
    InspectorTest.log('Calling main()');
    await WasmInspectorTest.evalWithUrl('instance.exports.main()', 'runWasm');
    InspectorTest.log('exports.main returned!');
  }
]);