Flutter Engine
The Flutter Engine
dwarf.cc
Go to the documentation of this file.
1// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
2// for details. All rights reserved. Use of this source code is governed by a
3// BSD-style license that can be found in the LICENSE file.
4
5#include "vm/dwarf.h"
6
7#include "vm/code_comments.h"
9#include "vm/elf.h"
10#include "vm/image_snapshot.h"
11#include "vm/object_store.h"
12
13namespace dart {
14
15#if defined(DART_PRECOMPILER)
16
17DEFINE_FLAG(bool,
18 resolve_dwarf_paths,
19 false,
20 "Resolve script URIs to absolute or relative file paths in DWARF");
21
23 write_code_comments_as_synthetic_source_to,
24 nullptr,
25 "Print comments associated with instructions into the given file");
26
27class DwarfPosition {
28 public:
29 DwarfPosition(int32_t line, int32_t column) : line_(line), column_(column) {
30 // Should only have no line information if also no column information.
31 ASSERT(line_ > kNoLine || column_ <= kNoColumn);
32 }
33 // CodeSourceMaps start the line and column registers at -1, not at 0, and
34 // the arguments passed to ChangePosition are retrieved from CodeSourceMaps.
35 explicit DwarfPosition(int32_t line) : DwarfPosition(line, -1) {}
36 constexpr DwarfPosition() : line_(-1), column_(-1) {}
37
38 // The DWARF standard uses 0 to denote missing line or column
39 // information.
40 static constexpr int32_t kNoLine = 0;
41 static constexpr int32_t kNoColumn = 0;
42
43 int32_t line() const { return line_ > kNoLine ? line_ : kNoLine; }
44 int32_t column() const { return column_ > kNoColumn ? column_ : kNoColumn; }
45
46 // Adjusts the contents given the arguments to a ChangePosition instruction
47 // from CodeSourceMaps.
48 void ChangePosition(int32_t line_delta, int32_t new_column) {
49 line_ = Utils::AddWithWrapAround(line_, line_delta);
50 column_ = new_column;
51 }
52
53 private:
54 int32_t line_;
55 int32_t column_;
56};
57
58static constexpr auto kNoDwarfPositionInfo = DwarfPosition();
59
60class InliningNode : public ZoneAllocated {
61 public:
62 InliningNode(const Function& function,
63 const DwarfPosition& position,
64 int32_t start_pc_offset)
66 position(position),
67 start_pc_offset(start_pc_offset),
68 end_pc_offset(-1),
69 children_head(nullptr),
70 children_tail(nullptr),
71 children_next(nullptr) {
72 ASSERT(!function.IsNull());
73 DEBUG_ASSERT(function.IsNotTemporaryScopedHandle());
74 }
75
76 void AppendChild(InliningNode* child) {
77 if (children_tail == nullptr) {
78 children_head = children_tail = child;
79 } else {
80 children_tail->children_next = child;
81 children_tail = child;
82 }
83 }
84
85 const Function& function;
86 DwarfPosition position;
87 int32_t start_pc_offset;
88 int32_t end_pc_offset;
89 InliningNode* children_head;
90 InliningNode* children_tail;
91 InliningNode* children_next;
92};
93
94Dwarf::Dwarf(Zone* zone, const Trie<const char>* deobfuscation_trie)
95 : zone_(zone),
96 deobfuscation_trie_(deobfuscation_trie),
97 codes_(zone, 1024),
98 code_to_label_(zone),
99 functions_(zone, 1024),
100 function_to_index_(zone),
101 scripts_(zone, 1024),
102 script_to_index_(zone) {}
103
104void Dwarf::AddCode(const Code& orig_code, intptr_t label) {
105 ASSERT(!orig_code.IsNull());
106 ASSERT(label > 0);
107
108 if (auto const old_pair = code_to_label_.Lookup(&orig_code)) {
109 // Dwarf objects can be shared, so we may get the same information for a
110 // given code object in different calls. In DEBUG mode, make sure the
111 // information is the same before returning.
112 ASSERT_EQUAL(label, old_pair->value);
113 return;
114 }
115
116 // Generate an appropriately zoned ZoneHandle for storing.
117 const auto& code = Code::ZoneHandle(zone_, orig_code.ptr());
118 codes_.Add(&code);
119 // Currently assumes the name has the same lifetime as the Zone of the
120 // Dwarf object (which is currently true). Otherwise, need to copy.
121 code_to_label_.Insert({&code, label});
122
123 if (code.IsFunctionCode() && !code.IsUnknownDartCode()) {
124 const Function& function = Function::Handle(zone_, code.function());
125 AddFunction(function);
126 }
127 const Array& inline_functions =
128 Array::Handle(zone_, code.inlined_id_to_function());
129 if (!inline_functions.IsNull()) {
130 Function& function = Function::Handle(zone_);
131 for (intptr_t i = 0; i < inline_functions.Length(); i++) {
132 function ^= inline_functions.At(i);
133 AddFunction(function);
134 }
135 }
136}
137
138intptr_t Dwarf::AddFunction(const Function& function) {
139 RELEASE_ASSERT(!function.IsNull());
140 FunctionIndexPair* pair = function_to_index_.Lookup(&function);
141 if (pair != nullptr) {
142 return pair->index_;
143 }
144 intptr_t index = functions_.length();
145 const Function& zone_func = Function::ZoneHandle(zone_, function.ptr());
146 function_to_index_.Insert(FunctionIndexPair(&zone_func, index));
147 functions_.Add(&zone_func);
148 const Script& script = Script::Handle(zone_, function.script());
149 AddScript(script);
150 return index;
151}
152
153intptr_t Dwarf::AddScript(const Script& script) {
154 RELEASE_ASSERT(!script.IsNull());
155 ScriptIndexPair* pair = script_to_index_.Lookup(&script);
156 if (pair != nullptr) {
157 return pair->index_;
158 }
159 // DWARF file numbers start from 1.
160 intptr_t index = scripts_.length() + 1;
161 const Script& zone_script = Script::ZoneHandle(zone_, script.ptr());
162 script_to_index_.Insert(ScriptIndexPair(&zone_script, index));
163 scripts_.Add(&zone_script);
164 return index;
165}
166
167intptr_t Dwarf::LookupFunction(const Function& function) {
168 RELEASE_ASSERT(!function.IsNull());
169 FunctionIndexPair* pair = function_to_index_.Lookup(&function);
170 if (pair == nullptr) {
171 FATAL("Function detected too late during DWARF generation: %s",
172 function.ToCString());
173 }
174 return pair->index_;
175}
176
177intptr_t Dwarf::LookupScript(const Script& script) {
178 RELEASE_ASSERT(!script.IsNull());
179 ScriptIndexPair* pair = script_to_index_.Lookup(&script);
180 if (pair == nullptr) {
181 FATAL("Script detected too late during DWARF generation: %s",
182 script.ToCString());
183 }
184 return pair->index_;
185}
186
187void Dwarf::WriteAbbreviations(DwarfWriteStream* stream) {
188 // Dwarf data mostly takes the form of a tree, whose nodes are called
189 // DIEs. Each DIE begins with an abbreviation code, and the abbreviation
190 // describes the attributes of that DIE and their representation.
191
192 stream->uleb128(kCompilationUnit); // Abbrev code.
193 stream->uleb128(DW_TAG_compile_unit); // Type.
194 stream->u1(DW_CHILDREN_yes);
195 stream->uleb128(DW_AT_name); // Start of attributes.
196 stream->uleb128(DW_FORM_string);
197 stream->uleb128(DW_AT_producer);
198 stream->uleb128(DW_FORM_string);
199 stream->uleb128(DW_AT_comp_dir);
200 stream->uleb128(DW_FORM_string);
201 stream->uleb128(DW_AT_low_pc);
202 stream->uleb128(DW_FORM_addr);
203 stream->uleb128(DW_AT_high_pc);
204 stream->uleb128(DW_FORM_addr);
205 stream->uleb128(DW_AT_stmt_list);
206 stream->uleb128(DW_FORM_sec_offset);
207 stream->uleb128(0);
208 stream->uleb128(0); // End of attributes.
209
210 stream->uleb128(kAbstractFunction); // Abbrev code.
211 stream->uleb128(DW_TAG_subprogram); // Type.
212 stream->u1(DW_CHILDREN_yes);
213 stream->uleb128(DW_AT_name); // Start of attributes.
214 stream->uleb128(DW_FORM_string);
215 stream->uleb128(DW_AT_decl_file);
216 stream->uleb128(DW_FORM_udata);
217 stream->uleb128(DW_AT_inline);
218 stream->uleb128(DW_FORM_udata);
219 stream->uleb128(0);
220 stream->uleb128(0); // End of attributes.
221
222 stream->uleb128(kConcreteFunction); // Abbrev code.
223 stream->uleb128(DW_TAG_subprogram); // Type.
224 stream->u1(DW_CHILDREN_yes);
225 stream->uleb128(DW_AT_abstract_origin); // Start of attributes.
226 stream->uleb128(DW_FORM_ref4);
227 stream->uleb128(DW_AT_low_pc);
228 stream->uleb128(DW_FORM_addr);
229 stream->uleb128(DW_AT_high_pc);
230 stream->uleb128(DW_FORM_addr);
231 stream->uleb128(DW_AT_artificial);
232 stream->uleb128(DW_FORM_flag);
233 stream->uleb128(0);
234 stream->uleb128(0); // End of attributes.
235
236 stream->uleb128(kInlinedFunction); // Abbrev code.
237 stream->uleb128(DW_TAG_inlined_subroutine); // Type.
238 stream->u1(DW_CHILDREN_yes);
239 stream->uleb128(DW_AT_abstract_origin); // Start of attributes.
240 stream->uleb128(DW_FORM_ref4);
241 stream->uleb128(DW_AT_low_pc);
242 stream->uleb128(DW_FORM_addr);
243 stream->uleb128(DW_AT_high_pc);
244 stream->uleb128(DW_FORM_addr);
245 stream->uleb128(DW_AT_call_file);
246 stream->uleb128(DW_FORM_udata);
247 stream->uleb128(DW_AT_call_line);
248 stream->uleb128(DW_FORM_udata);
249 stream->uleb128(DW_AT_call_column);
250 stream->uleb128(DW_FORM_udata);
251 stream->uleb128(DW_AT_artificial);
252 stream->uleb128(DW_FORM_flag);
253 stream->uleb128(0);
254 stream->uleb128(0); // End of attributes.
255
256 stream->uleb128(0); // End of abbreviations.
257}
258
259void Dwarf::WriteDebugInfo(DwarfWriteStream* stream) {
260 // 7.5.1.1 Compilation Unit Header
261
262 // Unit length.
263 stream->WritePrefixedLength("cu", [&]() {
264 stream->u2(2); // DWARF version 2
265 stream->u4(0); // debug_abbrev_offset
266 stream->u1(compiler::target::kWordSize); // address_size
267
268 // Compilation Unit DIE. We describe the entire Dart program as a single
269 // compilation unit. Note we write attributes in the same order we declared
270 // them in our abbreviation above in WriteAbbreviations.
271 stream->uleb128(kCompilationUnit);
272 const Library& root_library = Library::Handle(
273 zone_, IsolateGroup::Current()->object_store()->root_library());
274 const String& root_uri = String::Handle(zone_, root_library.url());
275 stream->string(root_uri.ToCString()); // DW_AT_name
276 stream->string("Dart VM"); // DW_AT_producer
277 stream->string(""); // DW_AT_comp_dir
278
279 // DW_AT_low_pc
280 // The lowest instruction address in this object file that is part of our
281 // compilation unit. Dwarf consumers use this to quickly decide which
282 // compilation unit DIE to consult for a given pc.
283 auto const isolate_instructions_label = ImageWriter::SectionLabel(
284 ImageWriter::ProgramSection::Text, /*vm=*/false);
285 stream->OffsetFromSymbol(isolate_instructions_label, 0);
286
287 // DW_AT_high_pc
288 // The highest instruction address in this object file that is part of our
289 // compilation unit. Dwarf consumers use this to quickly decide which
290 // compilation unit DIE to consult for a given pc.
291 if (codes_.is_empty()) {
292 // No code objects in this program, so set high_pc to same as low_pc.
293 stream->OffsetFromSymbol(isolate_instructions_label, 0);
294 } else {
295 const Code& last_code = *codes_.Last();
296 auto const last_code_label = code_to_label_.LookupValue(&last_code);
297 ASSERT(last_code_label > 0);
298 stream->OffsetFromSymbol(last_code_label, last_code.Size());
299 }
300
301 // DW_AT_stmt_list (offset into .debug_line)
302 // Indicates which line number program is associated with this compilation
303 // unit. We only emit a single line number program.
304 stream->u4(0);
305
306 WriteAbstractFunctions(stream);
307 WriteConcreteFunctions(stream);
308
309 stream->uleb128(0); // End of children.
310
311 stream->uleb128(0); // End of entries.
312 });
313}
314
315void Dwarf::WriteAbstractFunctions(DwarfWriteStream* stream) {
316 Script& script = Script::Handle(zone_);
317 String& name = String::Handle(zone_);
318 stream->InitializeAbstractOrigins(functions_.length());
319 // By the point we're creating DWARF information, scripts have already lost
320 // their token stream and we can't look up their line number or column
321 // information, hence the lack of DW_AT_decl_line and DW_AT_decl_column.
322 for (intptr_t i = 0; i < functions_.length(); i++) {
323 const Function& function = *(functions_[i]);
324 name = function.QualifiedUserVisibleName();
325 script = function.script();
326 const intptr_t file = LookupScript(script);
327 auto const name_cstr =
328 ImageWriter::Deobfuscate(zone_, deobfuscation_trie_, name.ToCString());
329
330 stream->RegisterAbstractOrigin(i);
331 stream->uleb128(kAbstractFunction);
332 stream->string(name_cstr); // DW_AT_name
333 stream->uleb128(file); // DW_AT_decl_file
334 stream->uleb128(DW_INL_inlined); // DW_AT_inline
335 stream->uleb128(0); // End of children.
336 }
337}
338
339void Dwarf::WriteConcreteFunctions(DwarfWriteStream* stream) {
340 Function& function = Function::Handle(zone_);
341 Script& script = Script::Handle(zone_);
342 for (intptr_t i = 0; i < codes_.length(); i++) {
343 const Code& code = *(codes_[i]);
344 RELEASE_ASSERT(!code.IsNull());
345 if (!code.IsFunctionCode() || code.IsUnknownDartCode()) {
346 continue;
347 }
348
349 function = code.function();
350 intptr_t function_index = LookupFunction(function);
351 script = function.script();
352 intptr_t label = code_to_label_.LookupValue(&code);
353 ASSERT(label > 0);
354
355 stream->uleb128(kConcreteFunction);
356 // DW_AT_abstract_origin
357 // References a node written above in WriteAbstractFunctions.
358 stream->AbstractOrigin(function_index);
359
360 // DW_AT_low_pc
361 stream->OffsetFromSymbol(label, 0);
362 // DW_AT_high_pc
363 stream->OffsetFromSymbol(label, code.Size());
364 // DW_AT_artificial
365 stream->u1(function.is_visible() ? 0 : 1);
366
367 InliningNode* node = ExpandInliningTree(code);
368 if (node != nullptr) {
369 for (InliningNode* child = node->children_head; child != nullptr;
370 child = child->children_next) {
371 WriteInliningNode(stream, child, label, script);
372 }
373 }
374
375 stream->uleb128(0); // End of children.
376 }
377}
378
379// Our state machine encodes position metadata such that we don't know the
380// end pc for an inlined function until it is popped, but DWARF DIEs encode
381// it where the function is pushed. We expand the state transitions into
382// an in-memory tree to do the conversion.
383InliningNode* Dwarf::ExpandInliningTree(const Code& code) {
384 const CodeSourceMap& map =
385 CodeSourceMap::Handle(zone_, code.code_source_map());
386 if (map.IsNull()) {
387 return nullptr;
388 }
389 const Array& functions = Array::Handle(zone_, code.inlined_id_to_function());
390 const Function& root_function = Function::ZoneHandle(zone_, code.function());
391 if (root_function.IsNull()) {
392 FATAL("Wherefore art thou functionless code, %s?\n", code.ToCString());
393 }
394
395 GrowableArray<InliningNode*> node_stack(zone_, 4);
396 GrowableArray<DwarfPosition> token_positions(zone_, 4);
397
398 NoSafepointScope no_safepoint;
399 ReadStream stream(map.Data(), map.Length());
400
401 int32_t current_pc_offset = 0;
402 token_positions.Add(kNoDwarfPositionInfo);
403 InliningNode* root_node =
404 new (zone_) InliningNode(root_function, token_positions.Last(), 0);
405 root_node->end_pc_offset = code.Size();
406 node_stack.Add(root_node);
407
408 while (stream.PendingBytes() > 0) {
409 int32_t arg1;
410 int32_t arg2 = -1;
411 const uint8_t opcode = CodeSourceMapOps::Read(&stream, &arg1, &arg2);
412 switch (opcode) {
413 case CodeSourceMapOps::kChangePosition: {
414 DwarfPosition& pos = token_positions[token_positions.length() - 1];
415 pos.ChangePosition(arg1, arg2);
416 break;
417 }
418 case CodeSourceMapOps::kAdvancePC: {
419 current_pc_offset += arg1;
420 if (arg1 == 0) {
421 // This happens at the start of the function where we emit a special
422 // kAdvancePC 0 instruction to record information about the function
423 // itself. We need to advance current_pc_offset a bit to prevent
424 // starting inlining interval directy at the start of the function
425 // itself.
426 current_pc_offset += 1;
427 }
428 break;
429 }
430 case CodeSourceMapOps::kPushFunction: {
431 const Function& child_func =
432 Function::ZoneHandle(zone_, Function::RawCast(functions.At(arg1)));
433 InliningNode* child_node = new (zone_)
434 InliningNode(child_func, token_positions.Last(), current_pc_offset);
435 node_stack.Last()->AppendChild(child_node);
436 node_stack.Add(child_node);
437 token_positions.Add(kNoDwarfPositionInfo);
438 break;
439 }
440 case CodeSourceMapOps::kPopFunction: {
441 // We never pop the root function.
442 ASSERT(node_stack.length() > 1);
443 ASSERT(token_positions.length() > 1);
444 node_stack.Last()->end_pc_offset = current_pc_offset;
445 node_stack.RemoveLast();
446 token_positions.RemoveLast();
447 break;
448 }
449 case CodeSourceMapOps::kNullCheck: {
450 break;
451 }
452 default:
453 UNREACHABLE();
454 }
455 }
456
457 while (node_stack.length() > 1) {
458 node_stack.Last()->end_pc_offset = current_pc_offset;
459 node_stack.RemoveLast();
460 token_positions.RemoveLast();
461 }
462 ASSERT(node_stack[0] == root_node);
463 return root_node;
464}
465
466void Dwarf::WriteInliningNode(DwarfWriteStream* stream,
467 InliningNode* node,
468 intptr_t root_label,
469 const Script& parent_script) {
470 ASSERT(root_label > 0);
471 intptr_t file = LookupScript(parent_script);
472 intptr_t function_index = LookupFunction(node->function);
473 const Script& script = Script::Handle(zone_, node->function.script());
474
475 stream->uleb128(kInlinedFunction);
476 // DW_AT_abstract_origin
477 // References a node written above in WriteAbstractFunctions.
478 stream->AbstractOrigin(function_index);
479
480 // DW_AT_low_pc
481 stream->OffsetFromSymbol(root_label, node->start_pc_offset);
482 // DW_AT_high_pc
483 stream->OffsetFromSymbol(root_label, node->end_pc_offset);
484 // DW_AT_call_file
485 stream->uleb128(file);
486
487 // DW_AT_call_line
488 stream->uleb128(node->position.line());
489 // DW_AT_call_column
490 stream->uleb128(node->position.column());
491 // DW_AT_artificial
492 stream->u1(node->function.is_visible() ? 0 : 1);
493
494 for (InliningNode* child = node->children_head; child != nullptr;
495 child = child->children_next) {
496 WriteInliningNode(stream, child, root_label, script);
497 }
498
499 stream->uleb128(0); // End of children.
500}
501
502// Helper class for tracking state of DWARF registers and emitting
503// line number program commands to set these registers to the right
504// state.
505class LineNumberProgramWriter {
506 public:
507 explicit LineNumberProgramWriter(DwarfWriteStream* stream)
508 : stream_(stream) {}
509
510 void EmitRow(intptr_t file,
511 intptr_t line,
512 intptr_t column,
513 intptr_t label,
514 intptr_t pc_offset) {
515 if (AddRow(file, line, column, label, pc_offset)) {
516 // Address register must be updated from 0 before emitting an LNP row
517 // (dartbug.com/41756).
518 stream_->u1(Dwarf::DW_LNS_copy);
519 }
520 }
521
522 // Associates the given file, line, and column information for the instruction
523 // at the pc_offset into the instructions payload of the Code object with the
524 // symbol asm_name. Returns whether any changes were made to the stream.
525 DART_WARN_UNUSED_RESULT bool AddRow(intptr_t file,
526 intptr_t line,
527 intptr_t column,
528 intptr_t label,
529 intptr_t pc_offset) {
530 ASSERT_EQUAL(end_sequence_, false);
531 bool source_info_changed = false;
532 // Note that files are 1-indexed.
533 ASSERT(file >= 1);
534 if (file != file_) {
535 stream_->u1(Dwarf::DW_LNS_set_file);
536 stream_->uleb128(file);
537 file_ = file;
538 source_info_changed = true;
539 }
540 ASSERT(line >= DwarfPosition::kNoLine);
541 if (line != line_) {
542 stream_->u1(Dwarf::DW_LNS_advance_line);
543 stream_->sleb128(line - line_);
544 line_ = line;
545 source_info_changed = true;
546 }
547 ASSERT(column >= DwarfPosition::kNoColumn);
548 if (column != column_) {
549 stream_->u1(Dwarf::DW_LNS_set_column);
550 stream_->uleb128(column);
551 column_ = column;
552 source_info_changed = true;
553 }
554 // If the file, line, and column information match that for the previous
555 // AddRow call, no change is made to the stream. This is because all
556 // addresses between two line number program rows inherit the source
557 // information from the first.
558 if (source_info_changed) {
559 SetCurrentPosition(label, pc_offset);
560 }
561 return source_info_changed;
562 }
563
564 void MarkEnd() {
565 ASSERT_EQUAL(end_sequence_, false);
566 // End of contiguous machine code.
567 stream_->u1(0); // This is an extended opcode
568 stream_->u1(1); // that is 1 byte long
569 stream_->u1(Dwarf::DW_LNE_end_sequence);
570 end_sequence_ = true;
571 }
572
573 void MarkEnd(intptr_t label, intptr_t pc_offset) {
574 ASSERT_EQUAL(end_sequence_, false);
575 SetCurrentPosition(label, pc_offset);
576 MarkEnd();
577 }
578
579 private:
580 void SetCurrentPosition(intptr_t label, intptr_t pc_offset) {
581 // Each LNP row is either in a different function from the previous row
582 // or is at an increasing PC offset into the same function.
583 ASSERT(label > 0);
584 ASSERT(pc_offset >= 0);
585 ASSERT(label_ != label || pc_offset > pc_offset_);
586 if (label_ != label) {
587 // Set the address register to the given offset into the new code payload.
588 auto const instr_size = 1 + compiler::target::kWordSize;
589 stream_->u1(0); // This is an extended opcode
590 stream_->u1(instr_size); // that is 5 or 9 bytes long
591 stream_->u1(Dwarf::DW_LNE_set_address);
592 stream_->OffsetFromSymbol(label, pc_offset);
593 } else {
594 // Change the address register by the difference in the two offsets.
595 stream_->u1(Dwarf::DW_LNS_advance_pc);
596 stream_->uleb128(pc_offset - pc_offset_);
597 }
598 label_ = label;
599 pc_offset_ = pc_offset;
600 }
601
602 DwarfWriteStream* const stream_;
603 // The initial values for the line number program state machine registers
604 // according to the DWARF standard.
605 intptr_t pc_offset_ = 0;
606 intptr_t file_ = 1;
607 intptr_t line_ = 1;
608 intptr_t column_ = 0;
609 bool end_sequence_ = false;
610
611 // Other info not stored in the state machine registers.
612 intptr_t label_ = 0;
613};
614
615void Dwarf::WriteSyntheticLineNumberProgram(LineNumberProgramWriter* writer) {
616 // We emit it last after all other scripts.
617 const intptr_t comments_file_index = scripts_.length() + 1;
618
619 auto file_open = Dart::file_open_callback();
620 auto file_write = Dart::file_write_callback();
621 auto file_close = Dart::file_close_callback();
622 if ((file_open == nullptr) || (file_write == nullptr) ||
623 (file_close == nullptr)) {
624 OS::PrintErr("warning: Could not access file callbacks.");
625 return;
626 }
627
628 TextBuffer comments_buffer(128 * KB);
629
630 const char* filename = FLAG_write_code_comments_as_synthetic_source_to;
631 void* comments_file = file_open(filename, /*write=*/true);
632 if (comments_file == nullptr) {
633 OS::PrintErr("warning: Failed to write code comments source: %s\n",
634 filename);
635 return;
636 }
637
638 // Lines in DWARF are 1-indexed.
639 intptr_t current_line = 1;
640
641 for (intptr_t i = 0; i < codes_.length(); i++) {
642 const Code& code = *(codes_[i]);
643 auto const label = code_to_label_.LookupValue(&code);
644 ASSERT(label > 0);
645
646 auto& comments = code.comments();
647 for (intptr_t i = 0, len = comments.Length(); i < len;) {
648 intptr_t current_pc_offset = comments.PCOffsetAt(i);
649 while (i < len && current_pc_offset == comments.PCOffsetAt(i)) {
650 comments_buffer.AddString(comments.CommentAt(i));
651 comments_buffer.AddChar('\n');
652 current_line++;
653 i++;
654 }
655 writer->EmitRow(comments_file_index, current_line - 1,
656 DwarfPosition::kNoColumn, label, current_pc_offset);
657 }
658 }
659
660 file_write(comments_buffer.buffer(), comments_buffer.length(), comments_file);
661 file_close(comments_file);
662}
663
664void Dwarf::WriteLineNumberProgramFromCodeSourceMaps(
665 LineNumberProgramWriter* writer) {
666 Function& root_function = Function::Handle(zone_);
667 Script& script = Script::Handle(zone_);
668 CodeSourceMap& map = CodeSourceMap::Handle(zone_);
669 Array& functions = Array::Handle(zone_);
670 GrowableArray<const Function*> function_stack(zone_, 8);
671 GrowableArray<DwarfPosition> token_positions(zone_, 8);
672
673 for (intptr_t i = 0; i < codes_.length(); i++) {
674 const Code& code = *(codes_[i]);
675 auto const label = code_to_label_.LookupValue(&code);
676 ASSERT(label > 0);
677
678 map = code.code_source_map();
679 if (map.IsNull()) {
680 continue;
681 }
682 root_function = code.function();
683 functions = code.inlined_id_to_function();
684
685 NoSafepointScope no_safepoint;
686 ReadStream code_map_stream(map.Data(), map.Length());
687
688 function_stack.Clear();
689 token_positions.Clear();
690
691 // CodeSourceMap might start in the following way:
692 //
693 // ChangePosition function.token_pos()
694 // AdvancePC 0
695 // ChangePosition x
696 // AdvancePC y
697 //
698 // This entry is emitted to ensure correct symbolization of
699 // function listener frames produced by async unwinding.
700 // (See EmitFunctionEntrySourcePositionDescriptorIfNeeded).
701 // Directly interpreting this sequence would cause us to emit
702 // multiple with the same pc into line number table and different
703 // position information. To avoid this will make an adjustment for
704 // the second record we emit: if position x is a synthetic one we will
705 // simply drop the second record, if position x is real then we will
706 // emit row with a slightly adjusted PC (by 1 byte). This would not
707 // affect symbolization (you can't have a call that is 1 byte long)
708 // but will avoid line number table entries with the same PC.
709 bool function_entry_position_was_emitted = false;
710
711 int32_t current_pc_offset = 0;
712 function_stack.Add(&root_function);
713 token_positions.Add(kNoDwarfPositionInfo);
714
715 while (code_map_stream.PendingBytes() > 0) {
716 int32_t arg1;
717 int32_t arg2 = -1;
718 const uint8_t opcode =
719 CodeSourceMapOps::Read(&code_map_stream, &arg1, &arg2);
720 switch (opcode) {
721 case CodeSourceMapOps::kChangePosition: {
722 DwarfPosition& pos = token_positions[token_positions.length() - 1];
723 pos.ChangePosition(arg1, arg2);
724 break;
725 }
726 case CodeSourceMapOps::kAdvancePC: {
727 // Emit a row for the previous PC value if the source location
728 // changed since the last row was emitted.
729 const Function& function = *(function_stack.Last());
730 script = function.script();
731 const intptr_t file = LookupScript(script);
732 const intptr_t line = token_positions.Last().line();
733 const intptr_t column = token_positions.Last().column();
734 intptr_t pc_offset_adjustment = 0;
735 bool should_emit = true;
736
737 // If we are at the function entry and have already emitted a row
738 // then adjust current_pc_offset to avoid duplicated entries.
739 // See the comment below which explains why this code is here.
740 if (current_pc_offset == 0 && function_entry_position_was_emitted) {
741 pc_offset_adjustment = 1;
742 // Ignore synthetic positions. Function entry position gives
743 // more information anyway.
744 should_emit = !(line == 0 && column == 0);
745 }
746
747 if (should_emit) {
748 writer->EmitRow(file, line, column, label,
749 current_pc_offset + pc_offset_adjustment);
750 }
751
752 current_pc_offset += arg1;
753 if (arg1 == 0) { // Special case of AdvancePC 0.
754 ASSERT(current_pc_offset == 0);
755 ASSERT(!function_entry_position_was_emitted);
756 function_entry_position_was_emitted = true;
757 }
758 break;
759 }
760 case CodeSourceMapOps::kPushFunction: {
761 auto child_func =
762 &Function::Handle(zone_, Function::RawCast(functions.At(arg1)));
763 function_stack.Add(child_func);
764 token_positions.Add(kNoDwarfPositionInfo);
765 break;
766 }
767 case CodeSourceMapOps::kPopFunction: {
768 // We never pop the root function.
769 ASSERT(function_stack.length() > 1);
770 ASSERT(token_positions.length() > 1);
771 function_stack.RemoveLast();
772 token_positions.RemoveLast();
773 break;
774 }
775 case CodeSourceMapOps::kNullCheck: {
776 break;
777 }
778 default:
779 UNREACHABLE();
780 }
781 }
782 }
783}
784
785static constexpr char kResolvedFileRoot[] = "file:///";
786static constexpr intptr_t kResolvedFileRootLen = sizeof(kResolvedFileRoot) - 1;
787static constexpr char kResolvedFlutterRoot[] = "org-dartlang-sdk:///flutter/";
788static constexpr intptr_t kResolvedFlutterRootLen =
789 sizeof(kResolvedFlutterRoot) - 1;
790static constexpr char kResolvedSdkRoot[] = "org-dartlang-sdk:///";
791static constexpr intptr_t kResolvedSdkRootLen = sizeof(kResolvedSdkRoot) - 1;
792static constexpr char kResolvedGoogle3Root[] = "google3:///";
793static constexpr intptr_t kResolvedGoogle3RootLen =
794 sizeof(kResolvedGoogle3Root) - 1;
795
796static const char* ConvertResolvedURI(const char* str) {
797 const intptr_t len = strlen(str);
798 if (len > kResolvedFileRootLen &&
799 strncmp(str, kResolvedFileRoot, kResolvedFileRootLen) == 0) {
800#if defined(DART_HOST_OS_WINDOWS)
801 return str + kResolvedFileRootLen; // Strip off the entire prefix.
802#else
803 return str + kResolvedFileRootLen - 1; // Leave a '/' on the front.
804#endif
805 }
806 // Must do kResolvedFlutterRoot before kResolvedSdkRoot, since the latter is
807 // a prefix of the former.
808 if (len > kResolvedFlutterRootLen &&
809 strncmp(str, kResolvedFlutterRoot, kResolvedFlutterRootLen) == 0) {
810 return str + kResolvedFlutterRootLen; // Strip off the entire prefix.
811 }
812 if (len > kResolvedSdkRootLen &&
813 strncmp(str, kResolvedSdkRoot, kResolvedSdkRootLen) == 0) {
814 return str + kResolvedSdkRootLen; // Strip off the entire prefix.
815 }
816 if (len > kResolvedGoogle3RootLen &&
817 strncmp(str, kResolvedGoogle3Root, kResolvedGoogle3RootLen) == 0) {
818 return str + kResolvedGoogle3RootLen; // Strip off the entire prefix.
819 }
820 return nullptr;
821}
822
823void Dwarf::WriteLineNumberProgram(DwarfWriteStream* stream) {
824 // 6.2.4 The Line Number Program Header
825
826 // 1. unit_length. This encoding implies 32-bit DWARF.
827 stream->WritePrefixedLength("line", [&]() {
828 stream->u2(2); // 2. DWARF version 2
829
830 // 3. header_length
831 stream->WritePrefixedLength("lineheader", [&]() {
832 stream->u1(1); // 4. minimum_instruction_length
833 stream->u1(1); // 5. default_is_stmt (true for dsymutil compatibility).
834 stream->u1(0); // 6. line_base
835 stream->u1(1); // 7. line_range
836 stream->u1(13); // 8. opcode_base (12 standard opcodes in Dwarf 2)
837
838 // 9. standard_opcode_lengths
839 stream->u1(0); // DW_LNS_copy, 0 operands
840 stream->u1(1); // DW_LNS_advance_pc, 1 operands
841 stream->u1(1); // DW_LNS_advance_list, 1 operands
842 stream->u1(1); // DW_LNS_set_file, 1 operands
843 stream->u1(1); // DW_LNS_set_column, 1 operands
844 stream->u1(0); // DW_LNS_negate_stmt, 0 operands
845 stream->u1(0); // DW_LNS_set_basic_block, 0 operands
846 stream->u1(0); // DW_LNS_const_add_pc, 0 operands
847 stream->u1(1); // DW_LNS_fixed_advance_pc, 1 operands
848 stream->u1(0); // DW_LNS_set_prolog_end, 0 operands
849 stream->u1(0); // DW_LNS_set_epilogue_begin, 0 operands
850 stream->u1(1); // DW_LNS_set_isa, 1 operands
851
852 // 10. include_directories (sequence of path names)
853 // We don't emit any because we use full paths below.
854 stream->u1(0);
855
856 // 11. file_names (sequence of file entries)
857 String& uri = String::Handle(zone_);
858 for (intptr_t i = 0; i < scripts_.length(); i++) {
859 const Script& script = *(scripts_[i]);
860 const char* uri_cstr = nullptr;
861 if (FLAG_resolve_dwarf_paths) {
862 uri = script.resolved_url();
863 // Strictly enforce this to catch unresolvable cases.
864 if (uri.IsNull()) {
865 FATAL("no resolved URI for Script %s available",
866 script.ToCString());
867 }
868 // resolved_url is never obfuscated, so just convert the prefix.
869 auto const orig_cstr = uri.ToCString();
870 auto const converted_cstr = ConvertResolvedURI(orig_cstr);
871 // Strictly enforce this to catch inconvertible cases.
872 if (converted_cstr == nullptr) {
873 FATAL("cannot convert resolved URI %s", orig_cstr);
874 }
875 uri_cstr = converted_cstr;
876 } else {
877 uri = script.url();
878 ASSERT(!uri.IsNull());
879 uri_cstr = ImageWriter::Deobfuscate(zone_, deobfuscation_trie_,
880 uri.ToCString());
881 }
882 RELEASE_ASSERT(strlen(uri_cstr) != 0);
883
884 stream->string(uri_cstr); // NOLINT
885 stream->uleb128(0); // Include directory index.
886 stream->uleb128(0); // File modification time.
887 stream->uleb128(0); // File length.
888 }
889 if (FLAG_write_code_comments_as_synthetic_source_to != nullptr) {
890 stream->string( // NOLINT
891 FLAG_write_code_comments_as_synthetic_source_to);
892 stream->uleb128(0); // Include directory index.
893 stream->uleb128(0); // File modification time.
894 stream->uleb128(0); // File length.
895 }
896 stream->u1(0); // End of file names.
897 });
898
899 // 6.2.5 The Line Number Program
900 LineNumberProgramWriter lnp_writer(stream);
901 if (FLAG_write_code_comments_as_synthetic_source_to != nullptr) {
902 WriteSyntheticLineNumberProgram(&lnp_writer);
903 } else {
904 WriteLineNumberProgramFromCodeSourceMaps(&lnp_writer);
905 }
906
907 // Advance pc to end of the compilation unit if not already there.
908 if (codes_.length() != 0) {
909 const intptr_t last_code_index = codes_.length() - 1;
910 const Code& last_code = *(codes_[last_code_index]);
911 const intptr_t last_pc_offset = last_code.Size();
912 auto const last_label = code_to_label_.LookupValue(&last_code);
913 ASSERT(last_label > 0);
914 lnp_writer.MarkEnd(last_label, last_pc_offset);
915 } else {
916 lnp_writer.MarkEnd();
917 }
918 });
919}
920
921#endif // DART_PRECOMPILER
922
923} // namespace dart
SkPoint pos
#define UNREACHABLE()
Definition: assert.h:248
#define DEBUG_ASSERT(cond)
Definition: assert.h:321
#define ASSERT_EQUAL(expected, actual)
Definition: assert.h:309
#define RELEASE_ASSERT(cond)
Definition: assert.h:327
static T AddWithWrapAround(T a, T b)
Definition: utils.h:431
#define DART_WARN_UNUSED_RESULT
Definition: dart_api.h:66
#define ASSERT(E)
#define FATAL(error)
const char * charp
Definition: flags.h:12
Dart_NativeFunction function
Definition: fuchsia.cc:51
SK_API bool Read(SkStreamSeekable *src, SkDocumentPage *dstArray, int dstArrayCount, const SkDeserialProcs *=nullptr)
Definition: dart_vm.cc:33
constexpr intptr_t KB
Definition: globals.h:528
DEFINE_FLAG(bool, print_cluster_information, false, "Print information about clusters written to snapshot")
constexpr intptr_t kWordSize
Definition: globals.h:509
DEF_SWITCHES_START aot vmservice shared library name
Definition: switches.h:32
SI auto map(std::index_sequence< I... >, Fn &&fn, const Args &... args) -> skvx::Vec< sizeof...(I), decltype(fn(args[0]...))>
Definition: SkVx.h:680