2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
19 #include "hphp/util/assertions.h"
21 #include <folly/Demangle.h>
22 #include <folly/Format.h>
23 #include <folly/Memory.h>
24 #include <folly/ScopeGuard.h>
25 #include <folly/String.h>
26 #include <folly/container/F14Map.h>
27 #include <folly/portability/Unistd.h>
28 #include <folly/experimental/symbolizer/Elf.h>
39 namespace debug_parser
{
41 ////////////////////////////////////////////////////////////////////////////////
44 * Thrown if there's an issue while parsing dwarf debug information.
46 struct DwarfStateException
: std::runtime_error
{
47 using std::runtime_error::runtime_error
;
51 GlobalOff(uint64_t off
, bool isInfo
) : m_value
{off
* 2 + (isInfo
? 1 : 0)} {
52 assert(offset() == off
);
54 GlobalOff(int64_t off
, bool isInfo
) :
55 GlobalOff
{ static_cast<uint64_t>(off
), isInfo
} {}
57 static GlobalOff
fromRaw(uint64_t raw
) {
58 return GlobalOff
{raw
};
60 uint64_t offset() const { return m_value
>> 1; }
61 bool isInfo() const { return m_value
& 1; }
62 uint64_t raw() const { return m_value
; }
64 friend GlobalOff
operator+(GlobalOff a
, size_t b
) {
65 return {a
.offset() + b
, a
.isInfo()};
67 friend bool operator<(GlobalOff a
, GlobalOff b
) {
68 // we want all the debug_types to sort before all the debug_infos
69 if ((a
.m_value
^ b
.m_value
) & 1) {
70 return (a
.m_value
& 1);
72 return a
.m_value
< b
.m_value
;
74 friend bool operator==(GlobalOff a
, GlobalOff b
) {
75 return a
.raw() == b
.raw();
77 friend bool operator>(GlobalOff a
, GlobalOff b
) { return b
< a
; }
78 friend bool operator<=(GlobalOff a
, GlobalOff b
) { return !(a
> b
); }
79 friend bool operator>=(GlobalOff a
, GlobalOff b
) { return !(a
< b
); }
80 friend bool operator!=(GlobalOff a
, GlobalOff b
) { return !(a
== b
); }
82 size_t hash(GlobalOff a
) const {
83 return std::hash
<uint64_t>{}(a
.raw());
85 bool equal(GlobalOff a
, GlobalOff b
) const {
88 size_t operator()(GlobalOff a
) const { return hash(a
); }
91 explicit GlobalOff(uint64_t raw
) : m_value
{raw
} {}
96 void build(folly::StringPiece debug_abbrev
);
98 static uint64_t readOne(folly::StringPiece
& section
,
99 uint64_t &tag
, bool &hasChildren
,
100 folly::StringPiece
& attrs
);
102 std::vector
<uint64_t> abbrev_vec
;
103 // map from offset to abbrev_vec index
104 folly::F14FastMap
<uint64_t, uint64_t> abbrev_map
;
108 using Sig8Map
= folly::F14FastMap
<uint64_t, GlobalOff
>;
110 explicit DwarfState(std::string filename
);
111 DwarfState(const DwarfState
&) = delete;
112 DwarfState(DwarfState
&&) = delete;
115 DwarfState
& operator=(const DwarfState
&) = delete;
116 DwarfState
& operator=(DwarfState
&&) = delete;
119 * A top level chunk in the .debug_info or .debug_types section.
121 * Contains a compilation-unit, or type-unit, and all its children.
123 * section + offset + size points at the start of the next context.
133 uint64_t abbrevOffset
;
134 uint64_t typeSignature
;
140 const Context
* context
;
141 // offset within context->section
145 // convenience copy from the context.
148 // offset from start to first attribute
150 // if we know where the next sibling is (eg via DW_AT_sibling), and
151 // it fits in uint32_t, the delta we need to add to offset to get
152 // there; otherwise zero.
153 uint32_t siblingDelta
;
154 // if we know where the next die is, and it fits in uint32_t, this
155 // is the delta we need to add to offset to get there; otherwise zero.
156 // if there are no children, this will be the same as sibling.
157 uint32_t nextDieDelta
;
158 folly::StringPiece attributes
;
161 struct AttributeSpec
{
165 explicit operator bool() const {
170 struct Attribute
: AttributeSpec
{
171 Attribute(AttributeSpec spec
, Die
* die
, folly::StringPiece sp
) :
172 AttributeSpec
{spec
}, die
{die
}, attrValue
{sp
} {}
174 folly::StringPiece attrValue
;
177 using Dwarf_Half
= uint16_t;
184 struct Dwarf_Ranges
{
185 static auto constexpr kSelection
= uintptr_t(-1);
191 Context
getContextAtOffset(GlobalOff off
) const;
192 Die
getDieAtOffset(const Context
* context
, GlobalOff off
) const;
193 Die
getNextSibling(Die
* die
) const;
194 Dwarf_Half
getTag(Die
* die
) const;
195 std::string
tagToString(Dwarf_Half tag
) const;
196 std::string
getDIEName(Die
* die
) const;
197 GlobalOff
getDIEOffset(Die
* die
) const;
198 Dwarf_Half
getAttributeType(Attribute
* attr
) const;
199 std::string
attributeTypeToString(Dwarf_Half type
) const;
200 Dwarf_Half
getAttributeForm(Attribute
* attr
) const;
201 std::string
attributeFormToString(Dwarf_Half form
) const;
202 std::string
opToString(Dwarf_Half form
) const;
203 std::string
getAttributeValueString(Attribute
* attr
) const;
204 folly::StringPiece
getAttributeValueStringPiece(Attribute
* attr
) const;
205 bool getAttributeValueFlag(Attribute
* attr
) const;
206 uint64_t getAttributeValueUData(Attribute
* attr
) const;
207 int64_t getAttributeValueSData(Attribute
* attr
) const;
208 uintptr_t getAttributeValueAddr(Attribute
* attr
) const;
209 GlobalOff
getAttributeValueRef(Attribute
* attr
) const;
210 uint64_t getAttributeValueSig8(Attribute
* attr
) const;
211 std::vector
<Dwarf_Loc
> getAttributeValueExprLoc(Attribute
* attr
) const;
212 std::vector
<Dwarf_Ranges
> getRanges(Attribute
* attr
) const;
214 // Get a string from the .debug_str section
215 folly::StringPiece
getStringFromStringSection(uint64_t offset
) const;
217 template <typename F
> void forEachContext(F
&& f
, bool isInfo
) const;
218 template <typename F
> void forEachContextParallel(F
&& f
, bool isInfo
,
219 int num_threads
) const;
220 template <typename F
> void forEachChild(Die
* die
, F
&& f
) const;
221 template <typename F
> void forEachAttribute(Die
* die
, F
&& f
) const;
222 template <typename F
> void forEachCompilationUnit(F
&& f
) const;
223 template <typename F
> void forEachTopLevelUnit(F
&& f
, bool isInfo
) const;
224 template <typename F
> void forEachTopLevelUnitParallel(F
&& f
, bool isInfo
,
225 int num_threads
) const;
226 template <typename F
> auto onDIEAtOffset(GlobalOff offset
, F
&& f
) const ->
227 decltype(f(std::declval
<Die
*>()));
228 template <typename F
> auto onDIEAtContextOffset(
229 GlobalOff contextOff
, F
&& f
) const ->
230 decltype(f(std::declval
<Die
*>()));
232 static AttributeSpec
readAttributeSpec(folly::StringPiece
&);
233 static Attribute
readAttribute(Die
* die
, AttributeSpec spec
,
234 folly::StringPiece
& sp
);
236 // Read (bitwise) one object of type T
238 static typename
std::enable_if
<std::is_pod
<T
>::value
, T
>::type
read(
239 folly::StringPiece
& sp
) {
240 assert(sp
.size() >= sizeof(T
));
242 memcpy(&x
, sp
.data(), sizeof(T
));
243 sp
.advance(sizeof(T
));
249 std::vector
<uint64_t> cuContextOffsets
;
250 std::vector
<uint64_t> tuContextOffsets
;
252 folly::symbolizer::ElfFile elf
;
253 folly::StringPiece debug_info
;
254 folly::StringPiece debug_types
;
255 folly::StringPiece debug_abbrev
;
256 folly::StringPiece debug_str
;
257 folly::StringPiece debug_ranges
;
259 // Read a value of "section offset" type, which may be 4 or 8 bytes
260 static uint64_t readOffset(folly::StringPiece
& sp
, bool is64Bit
) {
261 return is64Bit
? read
<uint64_t>(sp
) : read
<uint32_t>(sp
);
264 static void updateDelta(uint32_t& delta
, uint64_t value
) {
265 if (value
!= static_cast<uint32_t>(value
)) {
273 assertx(delta
== value
);
279 using Dwarf_Die
= DwarfState::Die
*;
280 using Dwarf_Context
= DwarfState::Context
*;
281 using Dwarf_Attribute
= DwarfState::Attribute
*;
284 * Iterate over all children of this DIE, calling the given callable for
285 * each. Iteration is stopped early if any of the calls return false.
287 template <typename F
>
288 void DwarfState::forEachChild(Dwarf_Die die
, F
&& f
) const {
289 if (!die
->hasChildren
) return;
291 if (!die
->nextDieDelta
) {
292 forEachAttribute(die
, [] (Dwarf_Attribute
) { return true; });
293 assert(die
->nextDieDelta
);
296 auto sibling
= getDieAtOffset(
297 die
->context
, { die
->offset
+ die
->nextDieDelta
, die
->context
->isInfo
}
299 while (sibling
.code
) {
300 if (!f(&sibling
)) return;
301 sibling
= getNextSibling(&sibling
);
304 // sibling is a dummy die whose offset is to the code 0 marking the
305 // end of the children. Need to add one to get the offset of the
307 updateDelta(die
->siblingDelta
, sibling
.offset
+ 1 - die
->offset
);
311 * Iterate over all attributes of the given DIE, calling the given callable for
312 * each. Iteration is stopped early if any of the calls return false.
314 template <typename F
>
315 void DwarfState::forEachAttribute(Dwarf_Die die
, F
&& f
) const {
316 auto attrs
= die
->attributes
;
317 auto values
= folly::StringPiece
{
318 die
->context
->section
+ die
->offset
+ die
->attrOffset
,
319 die
->context
->section
+ die
->context
->offset
+ die
->context
->size
321 while (auto const aspec
= readAttributeSpec(attrs
)) {
322 auto attr
= readAttribute(die
, aspec
, values
);
323 if (!die
->siblingDelta
&& attr
.name
== DW_AT_sibling
) {
324 updateDelta(die
->siblingDelta
,
325 getAttributeValueRef(&attr
).offset() - die
->offset
);
326 if (!die
->hasChildren
) {
327 assert(!die
->nextDieDelta
|| die
->nextDieDelta
== die
->siblingDelta
);
328 die
->nextDieDelta
= die
->siblingDelta
;
331 if (!f(&attr
)) return;
334 updateDelta(die
->nextDieDelta
,
335 values
.data() - die
->context
->section
- die
->offset
);
336 if (!die
->hasChildren
) {
337 assertx(!die
->siblingDelta
|| die
->siblingDelta
== die
->nextDieDelta
);
338 die
->siblingDelta
= die
->nextDieDelta
;
343 * Iterate over all the contexts in the file, calling the given
346 template <typename F
>
347 void DwarfState::forEachContext(F
&& f
, bool isInfo
) const {
348 auto const section
= isInfo
? debug_info
: debug_types
;
350 while (!sp
.empty()) {
351 auto context
= getContextAtOffset(
352 { sp
.data() - section
.data(), isInfo
}
354 sp
.advance(context
.size
);
360 * Iterate over all the contexts in the file, calling the given
361 * callable for each in parellel. The function f must be thread safe.
363 template <typename F
>
364 void DwarfState::forEachContextParallel(F
&& f
, bool isInfo
,
365 int num_threads
) const {
366 if (num_threads
<= 1) return forEachContext(f
, isInfo
);
368 std::vector
<GlobalOff
> offsets
;
370 forEachContext([&] (Dwarf_Context context
) {
371 offsets
.push_back({ context
->offset
, isInfo
});
374 std::atomic
<size_t> index
{0};
376 std::vector
<std::thread
> workers
;
377 for (auto worker
= size_t{0}; worker
< num_threads
; ++worker
) {
378 workers
.push_back(std::thread([&] {
380 const size_t kChunkSize
= 50;
381 auto start
= index
.fetch_add(kChunkSize
, std::memory_order_relaxed
);
382 if (start
>= offsets
.size()) break;
384 i
< start
+ kChunkSize
&& i
< offsets
.size();
386 auto context
= getContextAtOffset(offsets
[i
]);
392 for (auto& t
: workers
) t
.join();
396 * Iterate over all the compilation-units in the file, calling the given
399 template <typename F
>
400 void DwarfState::forEachTopLevelUnitParallel(F
&& f
, bool isInfo
,
401 int num_threads
) const {
402 forEachContextParallel(
403 [&] (Dwarf_Context context
) {
404 auto die
= getDieAtOffset(context
, { context
->firstDie
, isInfo
});
412 template <typename F
>
413 void DwarfState::forEachTopLevelUnit(F
&& f
, bool isInfo
) const {
414 forEachTopLevelUnitParallel(f
, isInfo
, 1);
417 template <typename F
> void DwarfState::forEachCompilationUnit(F
&& f
) const {
418 forEachTopLevelUnit(std::forward
<F
>(f
), true);
422 * Load the DIE at the given offset, and call the given callable on it,
423 * returning whatever the callable returns.
425 template <typename F
> auto DwarfState::onDIEAtOffset(GlobalOff offset
,
427 decltype(f(std::declval
<Dwarf_Die
>())) {
429 auto const& contextOffsets
= offset
.isInfo() ?
430 cuContextOffsets
: tuContextOffsets
;
431 auto it
= std::upper_bound(
432 contextOffsets
.begin(), contextOffsets
.end(), offset
.offset());
433 assertx(it
!= contextOffsets
.begin());
434 auto const contextOffset
= *--it
;
435 auto const context
= getContextAtOffset({ contextOffset
, offset
.isInfo() });
437 auto die
= getDieAtOffset(&context
, offset
);
441 template <typename F
>
442 auto DwarfState::onDIEAtContextOffset(GlobalOff offset
, F
&& f
) const ->
443 decltype(f(std::declval
<Dwarf_Die
>())) {
445 auto context
= getContextAtOffset(offset
);
446 auto die
= getDieAtOffset(&context
, { context
.firstDie
, context
.isInfo
});
450 ////////////////////////////////////////////////////////////////////////////////
454 ////////////////////////////////////////////////////////////////////////////////
457 template<> class FormatValue
<debug_parser::GlobalOff
> {
459 explicit FormatValue(debug_parser::GlobalOff val
) : m_val(val
) {}
461 template<typename Callback
>
462 void format(FormatArg
& arg
, Callback
& cb
) const {
463 format_value::formatString(folly::sformat("{}:{}",
465 m_val
.isInfo() ? 1 : 0),
470 debug_parser::GlobalOff m_val
;
476 struct hash
<debug_parser::GlobalOff
> : debug_parser::GlobalOff::Hash
{};
479 ////////////////////////////////////////////////////////////////////////////////