LLDB mainline
FormatterBytecode.cpp
Go to the documentation of this file.
1//===-- FormatterBytecode.cpp ---------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
13#include "llvm/ADT/StringExtras.h"
14#include "llvm/Support/DataExtractor.h"
15#include "llvm/Support/Error.h"
16#include "llvm/Support/Format.h"
17#include "llvm/Support/FormatProviders.h"
18#include "llvm/Support/FormatVariadicDetails.h"
19
20using namespace lldb;
21namespace lldb_private {
22
24 switch (op) {
25#define DEFINE_OPCODE(OP, MNEMONIC, NAME) \
26 case OP: { \
27 const char *s = MNEMONIC; \
28 return s ? s : #NAME; \
29 }
30#include "lldb/DataFormatters/FormatterBytecode.def"
31#undef DEFINE_OPCODE
32 }
33 return llvm::utostr(op);
34}
35
37 switch (sel) {
38#define DEFINE_SELECTOR(ID, NAME) \
39 case ID: \
40 return "@" #NAME;
41#include "lldb/DataFormatters/FormatterBytecode.def"
42#undef DEFINE_SELECTOR
43 }
44 return "@" + llvm::utostr(sel);
45}
46
48 switch (sig) {
49#define DEFINE_SIGNATURE(ID, NAME) \
50 case ID: \
51 return "@" #NAME;
52#include "lldb/DataFormatters/FormatterBytecode.def"
53#undef DEFINE_SIGNATURE
54 }
55 return llvm::utostr(sig);
56}
57
58std::string toString(const FormatterBytecode::DataStack &data) {
59 std::string s;
60 llvm::raw_string_ostream os(s);
61 os << "[ ";
62 for (auto &d : data) {
63 if (auto s = std::get_if<std::string>(&d))
64 os << '"' << *s << '"';
65 else if (auto u = std::get_if<uint64_t>(&d))
66 os << *u << 'u';
67 else if (auto i = std::get_if<int64_t>(&d))
68 os << *i;
69 else if (auto valobj = std::get_if<ValueObjectSP>(&d)) {
70 if (!valobj->get())
71 os << "null";
72 else
73 os << "object(" << valobj->get()->GetValueAsCString() << ')';
74 } else if (auto type = std::get_if<CompilerType>(&d)) {
75 os << '(' << type->GetTypeName(true) << ')';
76 } else if (auto sel = std::get_if<FormatterBytecode::Selectors>(&d)) {
77 os << toString(*sel);
78 }
79 os << ' ';
80 }
81 os << ']';
82 return s;
83}
84
85namespace FormatterBytecode {
86
87/// Implement the @format function.
88static llvm::Error FormatImpl(DataStack &data) {
89 auto fmt = data.Pop<std::string>();
90 auto replacements =
91 llvm::formatv_object_base::parseFormatString(fmt, 0, false);
92 std::string s;
93 llvm::raw_string_ostream os(s);
94 unsigned num_args = 0;
95 for (const auto &r : replacements)
96 if (r.Type == llvm::ReplacementType::Format)
97 num_args = std::max(num_args, r.Index + 1);
98
99 if (data.size() < num_args)
100 return llvm::createStringError("not enough arguments");
101
102 for (const auto &r : replacements) {
103 if (r.Type == llvm::ReplacementType::Literal) {
104 os << r.Spec;
105 continue;
106 }
107 using namespace llvm::support::detail;
108 auto arg = data[data.size() - num_args + r.Index];
109 auto format = [&](format_adapter &&adapter) {
110 llvm::FmtAlign Align(adapter, r.Where, r.Width, r.Pad);
111 Align.format(os, r.Options);
112 };
113
114 if (auto s = std::get_if<std::string>(&arg))
115 format(build_format_adapter(s->c_str()));
116 else if (auto u = std::get_if<uint64_t>(&arg))
117 format(build_format_adapter(u));
118 else if (auto i = std::get_if<int64_t>(&arg))
119 format(build_format_adapter(i));
120 else if (auto valobj = std::get_if<ValueObjectSP>(&arg)) {
121 if (!valobj->get())
122 format(build_format_adapter("null object"));
123 else
124 format(build_format_adapter(valobj->get()->GetValueAsCString()));
125 } else if (auto type = std::get_if<CompilerType>(&arg))
126 format(build_format_adapter(type->GetDisplayTypeName()));
127 else if (auto sel = std::get_if<FormatterBytecode::Selectors>(&arg))
128 format(build_format_adapter(toString(*sel)));
129 }
130 data.Push(s);
131 return llvm::Error::success();
132}
133
134static llvm::Error TypeCheck(llvm::ArrayRef<DataStackElement> data,
135 DataType type) {
136 if (data.size() < 1)
137 return llvm::createStringError("not enough elements on data stack");
138
139 auto &elem = data.back();
140 switch (type) {
141 case Any:
142 break;
143 case String:
144 if (!std::holds_alternative<std::string>(elem))
145 return llvm::createStringError("expected String");
146 break;
147 case UInt:
148 if (!std::holds_alternative<uint64_t>(elem))
149 return llvm::createStringError("expected UInt");
150 break;
151 case Int:
152 if (!std::holds_alternative<int64_t>(elem))
153 return llvm::createStringError("expected Int");
154 break;
155 case Object:
156 if (!std::holds_alternative<ValueObjectSP>(elem))
157 return llvm::createStringError("expected Object");
158 break;
159 case Type:
160 if (!std::holds_alternative<CompilerType>(elem))
161 return llvm::createStringError("expected Type");
162 break;
163 case Selector:
164 if (!std::holds_alternative<Selectors>(elem))
165 return llvm::createStringError("expected Selector");
166 break;
167 }
168 return llvm::Error::success();
169}
170
171static llvm::Error TypeCheck(llvm::ArrayRef<DataStackElement> data,
172 DataType type1, DataType type2) {
173 if (auto error = TypeCheck(data, type2))
174 return error;
175 return TypeCheck(data.drop_back(), type1);
176}
177
178static llvm::Error TypeCheck(llvm::ArrayRef<DataStackElement> data,
179 DataType type1, DataType type2, DataType type3) {
180 if (auto error = TypeCheck(data, type3))
181 return error;
182 return TypeCheck(data.drop_back(1), type2, type1);
183}
184
185llvm::Error Interpret(ControlStack &control, DataStack &data, Signatures sig) {
186 if (control.empty())
187 return llvm::Error::success();
188 // Since the only data types are single endian and ULEBs, the
189 // endianness should not matter.
190 llvm::DataExtractor cur_block(control.back(), true, 64);
191 llvm::DataExtractor::Cursor pc(0);
192
193 while (!control.empty()) {
194 /// Activate the top most block from the control stack.
195 auto activate_block = [&]() {
196 // Save the return address.
197 if (control.size() > 1)
198 control[control.size() - 2] = cur_block.getData().drop_front(pc.tell());
199 cur_block = llvm::DataExtractor(control.back(), true, 64);
200 if (pc)
201 pc = llvm::DataExtractor::Cursor(0);
202 };
203
204 /// Fetch the next byte in the instruction stream.
205 auto next_byte = [&]() -> uint8_t {
206 // At the end of the current block?
207 while (pc.tell() >= cur_block.size() && !control.empty()) {
208 if (control.size() == 1) {
209 control.pop_back();
210 return 0;
211 }
212 control.pop_back();
213 activate_block();
214 }
215
216 // Fetch the next instruction.
217 return cur_block.getU8(pc);
218 };
219
220 // Fetch the next opcode.
221 OpCodes opcode = (OpCodes)next_byte();
222 if (control.empty() || !pc)
223 return pc.takeError();
224
226 "[eval {0}] opcode={1}, control={2}, data={3}", toString(sig),
227 toString(opcode), control.size(), toString(data));
228
229 // Various shorthands to improve the readability of error handling.
230#define TYPE_CHECK(...) \
231 if (auto error = TypeCheck(data, __VA_ARGS__)) \
232 return error;
233
234 auto error = [&](llvm::Twine msg) {
235 return llvm::createStringError(msg + "(opcode=" + toString(opcode) + ")");
236 };
237
238 switch (opcode) {
239 // Data stack manipulation.
240 case op_dup:
242 data.Push(data.back());
243 continue;
244 case op_drop:
246 data.pop_back();
247 continue;
248 case op_pick: {
250 uint64_t idx = data.Pop<uint64_t>();
251 if (idx >= data.size())
252 return error("index out of bounds");
253 data.Push(data[idx]);
254 continue;
255 }
256 case op_over:
258 data.Push(data[data.size() - 2]);
259 continue;
260 case op_swap: {
262 auto x = data.PopAny();
263 auto y = data.PopAny();
264 data.Push(x);
265 data.Push(y);
266 continue;
267 }
268 case op_rot: {
270 auto z = data.PopAny();
271 auto y = data.PopAny();
272 auto x = data.PopAny();
273 data.Push(z);
274 data.Push(x);
275 data.Push(y);
276 continue;
277 }
278
279 // Control stack manipulation.
280 case op_begin: {
281 uint64_t length = cur_block.getULEB128(pc);
282 if (!pc)
283 return pc.takeError();
284 llvm::StringRef block = cur_block.getBytes(pc, length);
285 if (!pc)
286 return pc.takeError();
287 control.push_back(block);
288 continue;
289 }
290 case op_if:
292 if (data.Pop<uint64_t>() != 0) {
293 if (!cur_block.size())
294 return error("empty control stack");
295 activate_block();
296 } else
297 control.pop_back();
298 continue;
299 case op_ifelse:
301 if (cur_block.size() < 2)
302 return error("empty control stack");
303 if (data.Pop<uint64_t>() == 0)
304 control[control.size() - 2] = control.back();
305 control.pop_back();
306 activate_block();
307 continue;
308 case op_return:
309 control.clear();
310 return pc.takeError();
311
312 // Literals.
313 case op_lit_uint:
314 data.Push(cur_block.getULEB128(pc));
315 continue;
316 case op_lit_int:
317 data.Push(cur_block.getSLEB128(pc));
318 continue;
319 case op_lit_selector:
320 data.Push(Selectors(cur_block.getU8(pc)));
321 continue;
322 case op_lit_string: {
323 uint64_t length = cur_block.getULEB128(pc);
324 llvm::StringRef bytes = cur_block.getBytes(pc, length);
325 data.Push(bytes.str());
326 continue;
327 }
328 case op_as_uint: {
330 uint64_t casted;
331 int64_t val = data.Pop<int64_t>();
332 memcpy(&casted, &val, sizeof(val));
333 data.Push(casted);
334 continue;
335 }
336 case op_as_int: {
338 int64_t casted;
339 uint64_t val = data.Pop<uint64_t>();
340 memcpy(&casted, &val, sizeof(val));
341 data.Push(casted);
342 continue;
343 }
344 case op_is_null: {
346 data.Push(data.Pop<ValueObjectSP>() ? (uint64_t)0 : (uint64_t)1);
347 continue;
348 }
349
350 // Arithmetic, logic, etc.
351#define BINOP_IMPL(OP, CHECK_ZERO) \
352 { \
353 TYPE_CHECK(Any, Any); \
354 auto y = data.PopAny(); \
355 if (std::holds_alternative<uint64_t>(y)) { \
356 if (CHECK_ZERO && !std::get<uint64_t>(y)) \
357 return error(#OP " by zero"); \
358 TYPE_CHECK(UInt); \
359 data.Push((uint64_t)(data.Pop<uint64_t>() OP std::get<uint64_t>(y))); \
360 } else if (std::holds_alternative<int64_t>(y)) { \
361 if (CHECK_ZERO && !std::get<int64_t>(y)) \
362 return error(#OP " by zero"); \
363 TYPE_CHECK(Int); \
364 data.Push((int64_t)(data.Pop<int64_t>() OP std::get<int64_t>(y))); \
365 } else \
366 return error("unsupported data types"); \
367 }
368#define BINOP(OP) BINOP_IMPL(OP, false)
369#define BINOP_CHECKZERO(OP) BINOP_IMPL(OP, true)
370 case op_plus:
371 BINOP(+);
372 continue;
373 case op_minus:
374 BINOP(-);
375 continue;
376 case op_mul:
377 BINOP(*);
378 continue;
379 case op_div:
381 continue;
382 case op_mod:
384 continue;
385 case op_shl:
386#define SHIFTOP(OP, LEFT) \
387 { \
388 TYPE_CHECK(Any, UInt); \
389 uint64_t y = data.Pop<uint64_t>(); \
390 if (y > 64) \
391 return error("shift out of bounds"); \
392 if (std::holds_alternative<uint64_t>(data.back())) { \
393 uint64_t x = data.Pop<uint64_t>(); \
394 data.Push(x OP y); \
395 } else if (std::holds_alternative<int64_t>(data.back())) { \
396 int64_t x = data.Pop<int64_t>(); \
397 if (x < 0 && LEFT) \
398 return error("left shift of negative value"); \
399 if (y > 64) \
400 return error("shift out of bounds"); \
401 data.Push(x OP y); \
402 } else \
403 return error("unsupported data types"); \
404 }
405 SHIFTOP(<<, true);
406 continue;
407 case op_shr:
408 SHIFTOP(>>, false);
409 continue;
410 case op_and:
411 BINOP(&);
412 continue;
413 case op_or:
414 BINOP(|);
415 continue;
416 case op_xor:
417 BINOP(^);
418 continue;
419 case op_not:
421 data.Push(~data.Pop<uint64_t>());
422 continue;
423 case op_eq:
424 BINOP(==);
425 continue;
426 case op_neq:
427 BINOP(!=);
428 continue;
429 case op_lt:
430 BINOP(<);
431 continue;
432 case op_gt:
433 BINOP(>);
434 continue;
435 case op_le:
436 BINOP(<=);
437 continue;
438 case op_ge:
439 BINOP(>=);
440 continue;
441 case op_call: {
443 Selectors sel = data.Pop<Selectors>();
444
445 // Shorthand to improve readability.
446#define POP_VALOBJ(VALOBJ) \
447 auto VALOBJ = data.Pop<ValueObjectSP>(); \
448 if (!VALOBJ) \
449 return error("null object");
450
451 auto sel_error = [&](const char *msg) {
452 return llvm::createStringError("{0} (opcode={1}, selector={2})", msg,
453 toString(opcode).c_str(),
454 toString(sel).c_str());
455 };
456
457 switch (sel) {
458 case sel_summary: {
460 POP_VALOBJ(valobj);
461 const char *summary = valobj->GetSummaryAsCString();
462 data.Push(summary ? std::string(valobj->GetSummaryAsCString())
463 : std::string());
464 break;
465 }
466 case sel_get_num_children: {
468 POP_VALOBJ(valobj);
469 auto result = valobj->GetNumChildren();
470 if (!result)
471 return result.takeError();
472 data.Push((uint64_t)*result);
473 break;
474 }
475 case sel_get_child_at_index: {
477 auto index = data.Pop<uint64_t>();
478 POP_VALOBJ(valobj);
479 data.Push(valobj->GetChildAtIndex(index));
480 break;
481 }
482 case sel_get_child_with_name: {
484 auto name = data.Pop<std::string>();
485 POP_VALOBJ(valobj);
486 data.Push(valobj->GetChildMemberWithName(name));
487 break;
488 }
489 case sel_get_child_index: {
491 auto name = data.Pop<std::string>();
492 POP_VALOBJ(valobj);
493 if (auto index_or_err = valobj->GetIndexOfChildWithName(name))
494 data.Push((uint64_t)*index_or_err);
495 else
496 return index_or_err.takeError();
497 break;
498 }
499 case sel_get_type: {
501 POP_VALOBJ(valobj);
502 // FIXME: do we need to control dynamic type resolution?
503 data.Push(valobj->GetTypeImpl().GetCompilerType(false));
504 break;
505 }
506 case sel_get_template_argument_type: {
508 auto index = data.Pop<uint64_t>();
509 auto type = data.Pop<CompilerType>();
510 // FIXME: There is more code in SBType::GetTemplateArgumentType().
511 data.Push(type.GetTypeTemplateArgument(index, true));
512 break;
513 }
514 case sel_get_synthetic_value: {
516 POP_VALOBJ(valobj);
517 data.Push(valobj->GetSyntheticValue());
518 break;
519 }
520 case sel_get_non_synthetic_value: {
522 POP_VALOBJ(valobj);
523 data.Push(valobj->GetNonSyntheticValue());
524 break;
525 }
526 case sel_get_value: {
528 POP_VALOBJ(valobj);
529 data.Push(std::string(valobj->GetValueAsCString()));
530 break;
531 }
532 case sel_get_value_as_unsigned: {
534 POP_VALOBJ(valobj);
535 bool success;
536 uint64_t val = valobj->GetValueAsUnsigned(0, &success);
537 data.Push(val);
538 if (!success)
539 return sel_error("failed to get value");
540 break;
541 }
542 case sel_get_value_as_signed: {
544 POP_VALOBJ(valobj);
545 bool success;
546 int64_t val = valobj->GetValueAsSigned(0, &success);
547 data.Push(val);
548 if (!success)
549 return sel_error("failed to get value");
550 break;
551 }
552 case sel_get_value_as_address: {
554 POP_VALOBJ(valobj);
555 bool success;
556 uint64_t addr = valobj->GetValueAsUnsigned(0, &success);
557 if (!success)
558 return sel_error("failed to get value");
559 if (auto process_sp = valobj->GetProcessSP())
560 addr = process_sp->FixDataAddress(addr);
561 data.Push(addr);
562 break;
563 }
564 case sel_cast: {
566 auto type = data.Pop<CompilerType>();
567 POP_VALOBJ(valobj);
568 data.Push(valobj->Cast(type));
569 break;
570 }
571 case sel_strlen: {
573 data.Push((uint64_t)data.Pop<std::string>().size());
574 break;
575 }
576 case sel_fmt: {
578 if (auto error = FormatImpl(data))
579 return error;
580 break;
581 }
582 default:
583 return sel_error("selector not implemented");
584 }
585 continue;
586 }
587 }
588 return error("opcode not implemented");
589 }
590 return pc.takeError();
591}
592} // namespace FormatterBytecode
593
594} // namespace lldb_private
static llvm::raw_ostream & error(Stream &strm)
#define BINOP(OP)
#define SHIFTOP(OP, LEFT)
#define TYPE_CHECK(...)
#define POP_VALOBJ(VALOBJ)
#define BINOP_CHECKZERO(OP)
#define LLDB_LOGV(log,...)
Definition Log.h:383
Generic representation of a type in a programming language.
std::vector< ControlStackElement > ControlStack
static llvm::Error TypeCheck(llvm::ArrayRef< DataStackElement > data, DataType type)
static llvm::Error FormatImpl(DataStack &data)
Implement the @format function.
llvm::Error Interpret(ControlStack &control, DataStack &data, Signatures sig)
A class that represents a running process on the host machine.
Log * GetLog(Cat mask)
Retrieve the Log object for the channel associated with the given log enum.
Definition Log.h:332
static uint32_t Align(uint32_t val, uint32_t alignment)
Definition ARMUtils.h:21
std::string toString(FormatterBytecode::OpCodes op)
std::shared_ptr< lldb_private::ValueObject > ValueObjectSP