Optional Two-phase heap tracing
[hiphop-php.git] / hphp / util / type-scan.cpp
blob4c33a78d50fafca3f62e6edc24b264fd0ec2c583
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
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 +----------------------------------------------------------------------+
17 #include "hphp/util/type-scan.h"
19 #include <cstdio>
20 #include <dlfcn.h>
21 #include <memory>
22 #include <vector>
24 #include <folly/Format.h>
26 #include "hphp/util/embedded-data.h"
28 namespace {
30 ////////////////////////////////////////////////////////////////////////////////
32 // List of types which should be ignored (including any bases) by the generated
33 // scanners. Add as needed.
34 const std::unordered_set<std::string> ignored = {
35 "pthread_cond_t",
36 "std::condition_variable",
37 "st_mysql_bind",
40 // List of templates which should not be used to store request heap allocated
41 // values (because scanner aware variants exist).
42 const std::unordered_set<std::string> forbidden_template = {
43 "boost::container::flat_map",
44 "boost::container::flat_multimap",
45 "boost::container::flat_multiset",
46 "boost::container::flat_set",
47 "HPHP::FixedVector",
48 "HPHP::TinyVector",
49 "std::deque",
50 "std::forward_list",
51 "std::list",
52 "std::map",
53 "std::multimap",
54 "std::multiset",
55 "std::priority_queue",
56 "std::queue",
57 "std::set",
58 "std::shared_ptr",
59 "std::stack",
60 "std::unique_ptr",
61 "std::unordered_map",
62 "std::unordered_multimap",
63 "std::unordered_multiset",
64 "std::unordered_set",
65 "std::vector"
68 const std::unordered_set<std::string> forced_conservative = {
69 "boost::variant",
70 "folly::Optional",
71 "std::optional",
72 "std::function"
75 std::string stripTemplateArgs(std::string name) {
76 std::size_t open_count = 0;
77 std::size_t open_begin;
78 for (std::size_t i = 0; i < name.size();) {
79 if (name[i] == '<') {
80 if (!open_count) open_begin = i;
81 ++open_count;
82 ++i;
83 } else if (name[i] == '>' && open_count > 0) {
84 if (!--open_count) {
85 name = name.erase(open_begin, i - open_begin + 1);
86 i = open_begin;
87 } else {
88 ++i;
90 } else {
91 ++i;
94 return name;
97 ////////////////////////////////////////////////////////////////////////////////
101 namespace HPHP { namespace type_scan { namespace detail {
103 ////////////////////////////////////////////////////////////////////////////////
105 void conservative_stub(type_scan::Scanner& scanner,
106 const void* ptr,
107 std::size_t size) {
108 scanner.conservative(ptr, size);
111 void noptrs_stub(type_scan::Scanner& /*scanner*/, const void* /*ptr*/,
112 std::size_t /*size*/) {}
114 // Initialize metadata table, used before init() is called. Since before this,
115 // the only type-indices that can be present are "kIndexUnknown" and
116 // "kIndexUnknownNoPtrs".
117 const Metadata stub_metadata_table[] = {
118 {"(UNKNOWN)", conservative_stub},
119 {"(UNKNOWN NO-PTRS)", noptrs_stub}
121 const Metadata* g_metadata_table = stub_metadata_table;
122 std::size_t g_metadata_table_size = 2;
124 bool isIgnoredType(const std::string& name) {
125 return ignored.count(stripTemplateArgs(name));
128 bool isForbiddenTemplate(const std::string& name) {
129 return forbidden_template.count(stripTemplateArgs(name));
132 bool isForcedConservativeTemplate(const std::string& name) {
133 return forced_conservative.count(stripTemplateArgs(name));
136 ////////////////////////////////////////////////////////////////////////////////
140 using namespace detail;
142 void init() {
143 #if defined(__clang__)
144 // Clang is currently broken... It doesn't emit uncalled member functions in a
145 // template class, even when using ATTRIBUTE_USED. This prevents the custom
146 // scanners from being emitted (silently), which causes all sorts of
147 // problems. Punt for now until we can figure out a fix. This means that we'll
148 // continue to just conservative scan everything. See t10336705.
149 return;
150 #elif defined(__linux__) || defined(__FreeBSD__)
152 using init_func_t = const Metadata*(*)(std::size_t&);
154 auto const scanner_init = [] () -> init_func_t {
155 auto const result = dlsym(RTLD_DEFAULT, kInitFuncName);
156 if (result != nullptr) return reinterpret_cast<init_func_t>(result);
158 // Find the shared object embedded within a custom section.
159 embedded_data data;
160 if (!get_embedded_data("type_scanners", &data)) {
161 // no embedded data was built; fall back to conservative scan.
162 return nullptr;
165 // Link in the embedded object.
166 char tmp_filename[] = "/tmp/hhvm_type_scanner_XXXXXX";
167 auto const handle = dlopen_embedded_data(data, tmp_filename);
168 if (!handle) {
169 throw InitException{"Failed to dlopen embedded data"};
172 // Find the initialization function.
173 auto const init = dlsym(handle, kInitFuncName);
174 if (!init) {
175 throw InitException { folly::sformat("dlsym() fails: {}", dlerror()) };
177 return reinterpret_cast<init_func_t>(init);
178 }();
180 if (!scanner_init) return;
182 // And call it. The return value is a pointer to the new metadata table, and
183 // the size of the table will be updated by passing by ref.
184 if (auto const table = scanner_init(g_metadata_table_size)) {
185 g_metadata_table = table;
186 } else {
187 throw InitException{"Failed to load scanner table"};
189 #endif
191 // Some other platform... do nothing and continue to conservative scan for
192 // now.
195 ////////////////////////////////////////////////////////////////////////////////