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}",
227 toString(sig), toString(opcode), control.size(),
228 toString(data));
229
230 // Various shorthands to improve the readability of error handling.
231#define TYPE_CHECK(...) \
232 if (auto error = TypeCheck(data, __VA_ARGS__)) \
233 return error;
234
235 auto error = [&](llvm::Twine msg) {
236 return llvm::createStringError(msg + "(opcode=" + toString(opcode) + ")");
237 };
238
239 switch (opcode) {
240 // Data stack manipulation.
241 case op_dup:
243 data.Push(data.back());
244 continue;
245 case op_drop:
247 data.pop_back();
248 continue;
249 case op_pick: {
251 uint64_t idx = data.Pop<uint64_t>();
252 if (idx >= data.size())
253 return error("index out of bounds");
254 data.Push(data[idx]);
255 continue;
256 }
257 case op_over:
259 data.Push(data[data.size() - 2]);
260 continue;
261 case op_swap: {
263 auto x = data.PopAny();
264 auto y = data.PopAny();
265 data.Push(x);
266 data.Push(y);
267 continue;
268 }
269 case op_rot: {
271 auto z = data.PopAny();
272 auto y = data.PopAny();
273 auto x = data.PopAny();
274 data.Push(z);
275 data.Push(x);
276 data.Push(y);
277 continue;
278 }
279
280 // Control stack manipulation.
281 case op_begin: {
282 uint64_t length = cur_block.getULEB128(pc);
283 if (!pc)
284 return pc.takeError();
285 llvm::StringRef block = cur_block.getBytes(pc, length);
286 if (!pc)
287 return pc.takeError();
288 control.push_back(block);
289 continue;
290 }
291 case op_if:
293 if (data.Pop<uint64_t>() != 0) {
294 if (!cur_block.size())
295 return error("empty control stack");
296 activate_block();
297 } else
298 control.pop_back();
299 continue;
300 case op_ifelse:
302 if (cur_block.size() < 2)
303 return error("empty control stack");
304 if (data.Pop<uint64_t>() == 0)
305 control[control.size() - 2] = control.back();
306 control.pop_back();
307 activate_block();
308 continue;
309 case op_return:
310 control.clear();
311 return pc.takeError();
312
313 // Literals.
314 case op_lit_uint:
315 data.Push(cur_block.getULEB128(pc));
316 continue;
317 case op_lit_int:
318 data.Push(cur_block.getSLEB128(pc));
319 continue;
320 case op_lit_selector:
321 data.Push(Selectors(cur_block.getU8(pc)));
322 continue;
323 case op_lit_string: {
324 uint64_t length = cur_block.getULEB128(pc);
325 llvm::StringRef bytes = cur_block.getBytes(pc, length);
326 data.Push(bytes.str());
327 continue;
328 }
329 case op_as_uint: {
331 uint64_t casted;
332 int64_t val = data.Pop<int64_t>();
333 memcpy(&casted, &val, sizeof(val));
334 data.Push(casted);
335 continue;
336 }
337 case op_as_int: {
339 int64_t casted;
340 uint64_t val = data.Pop<uint64_t>();
341 memcpy(&casted, &val, sizeof(val));
342 data.Push(casted);
343 continue;
344 }
345 case op_is_null: {
347 data.Push(data.Pop<ValueObjectSP>() ? (uint64_t)0 : (uint64_t)1);
348 continue;
349 }
350
351 // Arithmetic, logic, etc.
352#define BINOP_IMPL(OP, CHECK_ZERO) \
353 { \
354 TYPE_CHECK(Any, Any); \
355 auto y = data.PopAny(); \
356 if (std::holds_alternative<uint64_t>(y)) { \
357 if (CHECK_ZERO && !std::get<uint64_t>(y)) \
358 return error(#OP " by zero"); \
359 TYPE_CHECK(UInt); \
360 data.Push((uint64_t)(data.Pop<uint64_t>() OP std::get<uint64_t>(y))); \
361 } else if (std::holds_alternative<int64_t>(y)) { \
362 if (CHECK_ZERO && !std::get<int64_t>(y)) \
363 return error(#OP " by zero"); \
364 TYPE_CHECK(Int); \
365 data.Push((int64_t)(data.Pop<int64_t>() OP std::get<int64_t>(y))); \
366 } else \
367 return error("unsupported data types"); \
368 }
369#define BINOP(OP) BINOP_IMPL(OP, false)
370#define BINOP_CHECKZERO(OP) BINOP_IMPL(OP, true)
371 case op_plus:
372 BINOP(+);
373 continue;
374 case op_minus:
375 BINOP(-);
376 continue;
377 case op_mul:
378 BINOP(*);
379 continue;
380 case op_div:
382 continue;
383 case op_mod:
385 continue;
386 case op_shl:
387#define SHIFTOP(OP, LEFT) \
388 { \
389 TYPE_CHECK(Any, UInt); \
390 uint64_t y = data.Pop<uint64_t>(); \
391 if (y > 64) \
392 return error("shift out of bounds"); \
393 if (std::holds_alternative<uint64_t>(data.back())) { \
394 uint64_t x = data.Pop<uint64_t>(); \
395 data.Push(x OP y); \
396 } else if (std::holds_alternative<int64_t>(data.back())) { \
397 int64_t x = data.Pop<int64_t>(); \
398 if (x < 0 && LEFT) \
399 return error("left shift of negative value"); \
400 if (y > 64) \
401 return error("shift out of bounds"); \
402 data.Push(x OP y); \
403 } else \
404 return error("unsupported data types"); \
405 }
406 SHIFTOP(<<, true);
407 continue;
408 case op_shr:
409 SHIFTOP(>>, false);
410 continue;
411 case op_and:
412 BINOP(&);
413 continue;
414 case op_or:
415 BINOP(|);
416 continue;
417 case op_xor:
418 BINOP(^);
419 continue;
420 case op_not:
422 data.Push(~data.Pop<uint64_t>());
423 continue;
424 case op_eq:
425 BINOP(==);
426 continue;
427 case op_neq:
428 BINOP(!=);
429 continue;
430 case op_lt:
431 BINOP(<);
432 continue;
433 case op_gt:
434 BINOP(>);
435 continue;
436 case op_le:
437 BINOP(<=);
438 continue;
439 case op_ge:
440 BINOP(>=);
441 continue;
442 case op_call: {
444 Selectors sel = data.Pop<Selectors>();
445
446 // Shorthand to improve readability.
447#define POP_VALOBJ(VALOBJ) \
448 auto VALOBJ = data.Pop<ValueObjectSP>(); \
449 if (!VALOBJ) \
450 return error("null object");
451
452 auto sel_error = [&](const char *msg) {
453 return llvm::createStringError("{0} (opcode={1}, selector={2})", msg,
454 toString(opcode).c_str(),
455 toString(sel).c_str());
456 };
457
458 switch (sel) {
459 case sel_summary: {
461 POP_VALOBJ(valobj);
462 const char *summary = valobj->GetSummaryAsCString();
463 data.Push(summary ? std::string(valobj->GetSummaryAsCString())
464 : std::string());
465 break;
466 }
467 case sel_get_num_children: {
469 POP_VALOBJ(valobj);
470 auto result = valobj->GetNumChildren();
471 if (!result)
472 return result.takeError();
473 data.Push((uint64_t)*result);
474 break;
475 }
476 case sel_get_child_at_index: {
478 auto index = data.Pop<uint64_t>();
479 POP_VALOBJ(valobj);
480 data.Push(valobj->GetChildAtIndex(index));
481 break;
482 }
483 case sel_get_child_with_name: {
485 auto name = data.Pop<std::string>();
486 POP_VALOBJ(valobj);
487 data.Push(valobj->GetChildMemberWithName(name));
488 break;
489 }
490 case sel_get_child_index: {
492 auto name = data.Pop<std::string>();
493 POP_VALOBJ(valobj);
494 if (auto index_or_err = valobj->GetIndexOfChildWithName(name))
495 data.Push((uint64_t)*index_or_err);
496 else
497 return index_or_err.takeError();
498 break;
499 }
500 case sel_get_type: {
502 POP_VALOBJ(valobj);
503 // FIXME: do we need to control dynamic type resolution?
504 data.Push(valobj->GetTypeImpl().GetCompilerType(false));
505 break;
506 }
507 case sel_get_template_argument_type: {
509 auto index = data.Pop<uint64_t>();
510 auto type = data.Pop<CompilerType>();
511 // FIXME: There is more code in SBType::GetTemplateArgumentType().
512 data.Push(type.GetTypeTemplateArgument(index, true));
513 break;
514 }
515 case sel_get_synthetic_value: {
517 POP_VALOBJ(valobj);
518 data.Push(valobj->GetSyntheticValue());
519 break;
520 }
521 case sel_get_non_synthetic_value: {
523 POP_VALOBJ(valobj);
524 data.Push(valobj->GetNonSyntheticValue());
525 break;
526 }
527 case sel_get_value: {
529 POP_VALOBJ(valobj);
530 data.Push(std::string(valobj->GetValueAsCString()));
531 break;
532 }
533 case sel_get_value_as_unsigned: {
535 POP_VALOBJ(valobj);
536 bool success;
537 uint64_t val = valobj->GetValueAsUnsigned(0, &success);
538 data.Push(val);
539 if (!success)
540 return sel_error("failed to get value");
541 break;
542 }
543 case sel_get_value_as_signed: {
545 POP_VALOBJ(valobj);
546 bool success;
547 int64_t val = valobj->GetValueAsSigned(0, &success);
548 data.Push(val);
549 if (!success)
550 return sel_error("failed to get value");
551 break;
552 }
553 case sel_get_value_as_address: {
555 POP_VALOBJ(valobj);
556 bool success;
557 uint64_t addr = valobj->GetValueAsUnsigned(0, &success);
558 if (!success)
559 return sel_error("failed to get value");
560 if (auto process_sp = valobj->GetProcessSP())
561 addr = process_sp->FixDataAddress(addr);
562 data.Push(addr);
563 break;
564 }
565 case sel_cast: {
567 auto type = data.Pop<CompilerType>();
568 POP_VALOBJ(valobj);
569 data.Push(valobj->Cast(type));
570 break;
571 }
572 case sel_strlen: {
574 data.Push((uint64_t)data.Pop<std::string>().size());
575 break;
576 }
577 case sel_fmt: {
579 if (auto error = FormatImpl(data))
580 return error;
581 break;
582 }
583 default:
584 return sel_error("selector not implemented");
585 }
586 continue;
587 }
588 }
589 return error("opcode not implemented");
590 }
591 return pc.takeError();
592}
593} // namespace FormatterBytecode
594
595} // 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_LOG_VERBOSE(log,...)
Definition Log.h:376
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