Optional Two-phase heap tracing
[hiphop-php.git] / hphp / util / hdf.cpp
blobe48a0f0eaed4860a325968173cbe479c82dc57d7
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/hdf.h"
19 #include <mutex>
21 #include <folly/portability/String.h>
23 #include <boost/algorithm/string/predicate.hpp>
25 namespace HPHP {
26 ///////////////////////////////////////////////////////////////////////////////
28 /**
29 * Helper class storing HDF raw pointer and reference counts on it.
31 struct HdfRaw {
32 static std::mutex HdfMutex;
34 HdfRaw() : m_hdf(nullptr), m_count(1) {
35 // ClearSilver is not thread-safe when calling hdf_init(), so guarding it.
36 std::lock_guard<std::mutex> lock(HdfMutex);
37 Hdf::CheckNeoError(hdf_init(&m_hdf));
38 assert(m_hdf);
40 ~HdfRaw() {
41 if (m_hdf) {
42 hdf_destroy(&m_hdf);
46 HDF *m_hdf;
47 int m_count;
49 void inc() { m_count++;}
50 void dec() { assert(m_count > 0); if (--m_count == 0) { delete this;}}
53 std::mutex HdfRaw::HdfMutex;
55 ///////////////////////////////////////////////////////////////////////////////
56 // constructors
58 Hdf::Hdf() : m_hdf(nullptr), m_dump(nullptr) {
59 m_rawp = new HdfRaw();
62 Hdf::Hdf(const char *filename) : m_hdf(nullptr), m_dump(nullptr) {
63 m_rawp = new HdfRaw();
64 append(filename);
67 Hdf::Hdf(const std::string &filename) : m_hdf(nullptr), m_dump(nullptr) {
68 m_rawp = new HdfRaw();
69 append(filename.c_str());
72 Hdf::Hdf(const Hdf *hdf, const char *name) : m_hdf(nullptr), m_dump(nullptr) {
73 assert(hdf);
74 assert(name && *name);
75 m_rawp = hdf->m_rawp;
76 if (m_rawp) {
77 m_rawp->inc();
78 m_path = hdf->getFullPath();
79 m_name = name;
80 } else {
81 assert(hdf->m_hdf);
82 hdf_get_node(hdf->m_hdf, (char*)name, &m_hdf);
86 Hdf::Hdf(const Hdf &hdf)
87 : m_hdf(hdf.m_hdf), m_rawp(hdf.m_rawp), m_path(hdf.m_path),
88 m_name(hdf.m_name), m_dump(nullptr) {
89 if (m_rawp) {
90 m_rawp->inc();
94 Hdf::Hdf(HDF *hdf)
95 : m_hdf(hdf), m_rawp(nullptr), m_dump(nullptr) {
98 Hdf::~Hdf() {
99 if (m_rawp) {
100 m_rawp->dec();
102 if (m_dump) {
103 free(m_dump);
107 bool Hdf::isEmpty() const {
108 return this->getFullPath() == "" &&
109 this->getName() == "";
112 void Hdf::assign(const Hdf &hdf) {
113 m_hdf = hdf.m_hdf;
114 if (m_rawp) {
115 m_rawp->dec();
117 m_rawp = hdf.m_rawp;
118 if (m_rawp) {
119 m_rawp->inc();
121 m_path = hdf.m_path;
122 m_name = hdf.m_name;
123 if (m_dump) {
124 free(m_dump);
125 m_dump = nullptr;
129 void Hdf::copy(const Hdf &hdf) {
130 CheckNeoError(hdf_copy(getRaw(), nullptr, hdf.getRaw()));
133 void Hdf::open(const char *filename) {
134 close();
135 append(filename);
138 void Hdf::append(const char *filename) {
139 assert(filename && *filename);
140 if (!(boost::contains(filename, ".hdf")
141 || boost::ends_with(filename, ".hphp"))) {
142 return;
144 CheckNeoError(hdf_read_file(getRaw(), (char*)filename));
147 void Hdf::close() {
148 m_hdf = nullptr;
149 if (m_rawp) {
150 m_rawp->dec();
151 m_rawp = new HdfRaw();
153 m_path.clear();
154 m_name.clear();
155 if (m_dump) {
156 free(m_dump);
157 m_dump = nullptr;
161 static bool match(const std::string &name, const std::string &pattern) {
162 assert(!name.empty() && !pattern.empty());
164 unsigned int len = pattern.size();
165 char first = pattern[0];
166 char last = pattern[len - 1];
167 if (first == '*') {
168 if (last == '*') {
169 return name.find(pattern.substr(1, len - 2)) != std::string::npos;
171 return name.size() >= len - 1 &&
172 name.substr(name.size() - len + 1) == pattern.substr(1);
174 if (last == '*') {
175 return strncmp(name.c_str(), pattern.c_str(), len - 1) == 0;
177 return name == pattern;
180 bool Hdf::lintImpl(std::vector<std::string> &names,
181 const std::vector<std::string> &excludes, bool visited) {
182 unsigned int size = names.size();
184 bool childVisited = false;
185 for (Hdf hdf = firstChild(false); hdf.exists(); hdf = hdf.next(false)) {
186 if (hdf.lintImpl(names, excludes, visited)) {
187 childVisited = true;
190 bool meVisited = childVisited || hdf_is_visited(getRaw());
192 std::string fullname = getFullPath();
193 if (!fullname.empty()) {
194 if (meVisited == visited) {
195 bool excluded = false;
196 for (unsigned int i = 0; i < excludes.size(); i++) {
197 if (match(fullname, excludes[i])) {
198 excluded = true;
199 break;
202 if (!excluded) {
203 if (!visited) {
204 names.resize(size); // so reports about my children are gone
206 names.push_back(fullname);
211 return meVisited;
214 void Hdf::lint(std::vector<std::string> &names,
215 const char *excludePatternNode /* = "LintExcludePatterns" */,
216 bool visited /* = false */) {
217 std::vector<std::string> patterns;
218 if (excludePatternNode && *excludePatternNode) {
219 for (Hdf hdf = operator[](excludePatternNode).firstChild();
220 hdf.exists(); hdf = hdf.next()) {
221 std::string value = hdf.configGetString();
222 if (!value.empty()) {
223 patterns.push_back(value);
228 lintImpl(names, patterns, visited);
231 void Hdf::setVisited(bool visited /* = true */) {
232 hdf_set_visited(getRaw(), visited ? 1 : 0);
233 for (Hdf hdf = firstChild(false); hdf.exists(); hdf = hdf.next(false)) {
234 hdf.setVisited(visited);
238 ///////////////////////////////////////////////////////////////////////////////
239 // gets
241 const char *Hdf::configGet(const char *defValue /* = NULL */) const {
242 HDF *hdf = getRaw();
243 const char *v = hdf_obj_value(hdf);
244 hdf_set_visited(hdf, 1);
245 return v ? v : defValue;
248 std::string Hdf::configGetString(const std::string &defValue /* = "" */) const {
249 const char *v = configGet();
250 if (v == nullptr) return defValue;
251 return v;
254 bool Hdf::configGetBool(bool defValue /* = false */) const {
255 const char *v = configGet();
256 if (v == nullptr) return defValue;
257 return convertRawConfigToBool(v);
260 int64_t Hdf::getInt(int64_t defValue, const char *type, int64_t maxValue) const {
261 const char *v = configGet();
262 if (v == nullptr) return defValue;
264 char *endptr = nullptr;
265 int64_t n = strtoll(v, &endptr, 0);
266 if ((!endptr && !*endptr) ||
267 (maxValue && (n > maxValue || n < (- maxValue - 1)))) {
268 throw HdfDataTypeException(this, type, v);
271 return n;
274 char Hdf::configGetByte(char defValue /* = 0 */) const {
275 return getInt(defValue, "byte", 0x7FL);
278 int16_t Hdf::configGetInt16(int16_t defValue /* = 0 */) const {
279 return getInt(defValue, "int16", 0x7FFFL);
282 int32_t Hdf::configGetInt32(int32_t defValue /* = 0 */) const {
283 return getInt(defValue, "int32", 0x7FFFFFFFL);
286 int64_t Hdf::configGetInt64(int64_t defValue /* = 0 */) const {
287 return getInt(defValue, "int64", 0);
290 uint64_t Hdf::getUInt(uint64_t defValue, const char *type, uint64_t mask) const {
291 const char *v = configGet();
292 if (v == nullptr) return defValue;
294 char *endptr = nullptr;
295 int64_t n = strtoull(v, &endptr, 0);
296 if ((!endptr && !*endptr) || (mask && ((uint64_t)n & mask))) {
297 throw HdfDataTypeException(this, type, v);
300 return n;
303 unsigned char Hdf::configGetUByte(unsigned char defValue /* = 0 */) const {
304 return getUInt(defValue, "unsigned byte", ~0xFFUL);
307 uint16_t Hdf::configGetUInt16(uint16_t defValue /* = 0 */) const {
308 return getUInt(defValue, "unsigned int16", ~0xFFFFUL);
311 uint32_t Hdf::configGetUInt32(uint32_t defValue /* = 0 */) const {
312 return getUInt(defValue, "unsigned int32", ~0xFFFFFFFFUL);
315 uint64_t Hdf::configGetUInt64(uint64_t defValue /* = 0 */) const {
316 return getUInt(defValue, "unsigned int64", 0);
319 double Hdf::configGetDouble(double defValue /* = 0 */) const {
320 const char *v = configGet();
321 if (v == nullptr) return defValue;
323 char *endptr = nullptr;
324 double n = strtod(v, &endptr);
325 if (!endptr && !*endptr) {
326 throw HdfDataTypeException(this, "double", v);
329 return n;
332 void Hdf::configGet(std::vector<std::string> &values) const {
333 values.clear();
334 for (Hdf hdf = firstChild(); hdf.exists(); hdf = hdf.next()) {
335 values.push_back(hdf.configGetString(""));
339 void Hdf::configGet(std::set<std::string> &values) const {
340 values.clear();
341 for (Hdf hdf = firstChild(); hdf.exists(); hdf = hdf.next()) {
342 values.insert(hdf.configGetString(""));
346 void Hdf::configGet(boost::container::flat_set<std::string> &values) const {
347 values.clear();
348 for (Hdf hdf = firstChild(); hdf.exists(); hdf = hdf.next()) {
349 values.insert(hdf.configGetString(""));
353 void Hdf::configGet(std::set<std::string, stdltistr> &values) const {
354 values.clear();
355 for (Hdf hdf = firstChild(); hdf.exists(); hdf = hdf.next()) {
356 values.insert(hdf.configGetString(""));
360 void Hdf::configGet(std::map<std::string, std::string> &values) const {
361 values.clear();
362 for (Hdf hdf = firstChild(); hdf.exists(); hdf = hdf.next()) {
363 values[hdf.getName()] = hdf.configGetString("");
367 void Hdf::configGet(std::map<std::string, std::string,
368 stdltistr> &values) const {
369 values.clear();
370 for (Hdf hdf = firstChild(); hdf.exists(); hdf = hdf.next()) {
371 values[hdf.getName()] = hdf.configGetString("");
375 void Hdf::configGet(hphp_string_imap<std::string> &values) const {
376 values.clear();
377 for (Hdf hdf = firstChild(); hdf.exists(); hdf = hdf.next()) {
378 values[hdf.getName()] = hdf.configGetString("");
382 bool Hdf::convertRawConfigToBool(const char *v) {
383 return *v && strcmp(v, "0") &&
384 strcasecmp(v, "false") && strcasecmp(v, "no") && strcasecmp(v, "off");
387 int Hdf::compare(const char *v2) const {
388 const char *v1 = configGet();
389 if (v1 == nullptr && v2 == nullptr) return 0;
390 if (v1 == nullptr) return -1;
391 if (v2 == nullptr) return 1;
392 return strcmp(v1, v2);
395 int Hdf::compare(const std::string &v2) const {
396 std::string v1 = configGetString();
397 return strcmp(v1.c_str(), v2.c_str());
400 int Hdf::compare(char v2) const {
401 char v1 = configGetByte();
402 if (v1 == v2) return 0;
403 return v1 > v2 ? 1 : -1;
406 int Hdf::compare(unsigned char v2) const {
407 unsigned char v1 = configGetUByte();
408 if (v1 == v2) return 0;
409 return v1 > v2 ? 1 : -1;
412 int Hdf::compare(int16_t v2) const {
413 int16_t v1 = configGetInt16();
414 if (v1 == v2) return 0;
415 return v1 > v2 ? 1 : -1;
418 int Hdf::compare(uint16_t v2) const {
419 uint16_t v1 = configGetUInt16();
420 if (v1 == v2) return 0;
421 return v1 > v2 ? 1 : -1;
424 int Hdf::compare(int32_t v2) const {
425 int32_t v1 = configGetInt32();
426 if (v1 == v2) return 0;
427 return v1 > v2 ? 1 : -1;
430 int Hdf::compare(uint32_t v2) const {
431 uint32_t v1 = configGetUInt32();
432 if (v1 == v2) return 0;
433 return v1 > v2 ? 1 : -1;
436 int Hdf::compare(int64_t v2) const {
437 int64_t v1 = configGetInt64();
438 if (v1 == v2) return 0;
439 return v1 > v2 ? 1 : -1;
442 int Hdf::compare(uint64_t v2) const {
443 uint64_t v1 = configGetUInt64();
444 if (v1 == v2) return 0;
445 return v1 > v2 ? 1 : -1;
448 int Hdf::compare(double v2) const {
449 double v1 = configGetDouble();
450 if (v1 == v2) return 0;
451 return v1 > v2 ? 1 : -1;
454 ///////////////////////////////////////////////////////////////////////////////
455 // sets
457 Hdf &Hdf::operator=(const Hdf &hdf) {
458 if (&hdf != this) {
459 if (m_rawp != hdf.m_rawp) {
460 if (m_rawp) {
461 m_rawp->dec();
463 m_rawp = hdf.m_rawp;
464 if (m_rawp) {
465 m_rawp->inc();
469 m_hdf = hdf.m_hdf;
470 m_path = hdf.m_path;
471 m_name = hdf.m_name;
473 if (m_dump) {
474 free(m_dump);
476 m_dump = nullptr;
478 return *this;
481 void Hdf::set(const char *value) {
482 CheckNeoError(hdf_set_value(getRaw(), nullptr, (char*)value));
485 void Hdf::set(int64_t value) {
486 char buf[24];
487 snprintf(buf, sizeof(buf), "%lld", (long long)value);
488 set(buf);
491 void Hdf::set(uint64_t value) {
492 char buf[24];
493 snprintf(buf, sizeof(buf), "%llu", (unsigned long long)value);
494 set(buf);
497 void Hdf::set(double value) {
498 char buf[32];
499 snprintf(buf, sizeof(buf), "%g", value);
500 set(buf);
503 ///////////////////////////////////////////////////////////////////////////////
504 // sub-nodes
506 std::string Hdf::getName(bool markVisited /* = true */) const {
507 HDF *hdf = getRaw();
508 char *name = hdf_obj_name(hdf);
509 if (markVisited) hdf_set_visited(hdf, 1);
510 return name ? name : "";
513 std::string Hdf::getFullPath() const {
514 std::string fullpath;
515 if (m_path.empty()) {
516 fullpath = m_name;
517 } else {
518 fullpath = m_path;
519 if (!m_name.empty()) {
520 fullpath += ".";
521 fullpath += m_name;
524 return fullpath;
527 Hdf Hdf::parentImpl() const {
528 Hdf hdf(*this);
529 if (m_name.empty()) {
530 if (m_path.empty()) {
531 throw HdfInvalidOperation("calling parent() on topmost node");
533 size_t pos = m_path.rfind('.');
534 if (pos == std::string::npos) {
535 hdf.m_name = m_path;
536 hdf.m_path.clear();
537 } else {
538 hdf.m_name = m_path.substr(pos + 1);
539 hdf.m_path = m_path.substr(0, pos);
541 } else {
542 hdf.m_name.clear();
544 return hdf;
547 const Hdf Hdf::parent() const {
548 return parentImpl();
551 Hdf Hdf::parent() {
552 return parentImpl();
555 const Hdf Hdf::operator[](int name) const {
556 char buf[12];
557 sprintf(buf, "%d", name);
558 return operator[](buf);
561 const Hdf Hdf::operator[](const char *name) const {
562 return Hdf(this, name);
565 const Hdf Hdf::operator[](const std::string &name) const {
566 return operator[](name.c_str());
569 Hdf Hdf::operator[](int name) {
570 char buf[12];
571 sprintf(buf, "%d", name);
572 return operator[](buf);
575 Hdf Hdf::operator[](const char *name) {
576 return Hdf(this, name);
579 Hdf Hdf::operator[](const std::string &name) {
580 return operator[](name.c_str());
583 bool Hdf::exists() const {
584 if (m_rawp == nullptr) {
585 return m_hdf != nullptr;
588 std::string fullpath = getFullPath();
589 if (fullpath.empty()) {
590 return true;
592 return hdf_get_obj(m_rawp->m_hdf, fullpath.c_str());
595 bool Hdf::exists(int name) const {
596 char buf[12];
597 sprintf(buf, "%d", name);
598 return exists(buf);
601 bool Hdf::exists(const char *name) const {
602 HDF *hdf = m_hdf;
603 if (m_rawp) {
604 std::string fullpath = getFullPath();
605 hdf = m_rawp->m_hdf;
606 if (!fullpath.empty()) {
607 hdf = hdf_get_obj(hdf, fullpath.c_str());
610 return hdf && hdf_get_obj(hdf, name);
613 bool Hdf::exists(const std::string &name) const {
614 return exists(name.c_str());
617 void Hdf::remove(int name) const {
618 char buf[12];
619 sprintf(buf, "%d", name);
620 remove(buf);
623 void Hdf::remove(const char *name) const {
624 assert(name && *name);
625 CheckNeoError(hdf_remove_tree(getRaw(), name));
628 void Hdf::remove(const std::string &name) const {
629 remove(name.c_str());
632 ///////////////////////////////////////////////////////////////////////////////
633 // iterations
635 Hdf Hdf::firstChild(bool markVisited /* = true */) const {
636 HDF *hdf = getRaw();
637 if (markVisited) hdf_set_visited(hdf, 1);
638 Hdf ret(hdf_obj_child(hdf));
639 ret.m_path = getFullPath();
640 ret.m_name = ret.getName(markVisited);
641 return ret;
644 Hdf Hdf::next(bool markVisited /* = true */) const {
645 HDF *hdf = getRaw();
646 if (markVisited) hdf_set_visited(hdf, 1);
647 Hdf ret(hdf_obj_next(hdf));
648 ret.m_path = m_path;
649 ret.m_name = ret.getName(markVisited);
650 return ret;
653 ///////////////////////////////////////////////////////////////////////////////
654 // input/output
656 void Hdf::fromString(const char *input) {
657 CheckNeoError(hdf_read_string(getRaw(), (char*)input));
660 const char *Hdf::toString() const {
661 if (m_dump) {
662 free(m_dump);
663 m_dump = nullptr;
665 CheckNeoError(hdf_write_string(getRaw(), &m_dump));
666 return m_dump;
669 void Hdf::write(const char *filename) const {
670 CheckNeoError(hdf_write_file(getRaw(), filename));
673 ///////////////////////////////////////////////////////////////////////////////
674 // helpers
676 HDF *Hdf::getRaw() const {
677 if (m_hdf) return m_hdf;
679 if (m_rawp == nullptr) {
680 return nullptr;
683 HDF *ret = nullptr;
684 std::string fullpath = getFullPath();
685 if (fullpath.empty()) {
686 ret = m_rawp->m_hdf;
687 } else {
688 hdf_get_node(m_rawp->m_hdf, (char*)fullpath.c_str(), &ret);
690 m_hdf = ret;
691 return ret;
694 void Hdf::CheckNeoError(NEOERR *err) {
695 if (err != STATUS_OK) {
696 NEOSTRING str;
697 string_init(&str);
698 nerr_error_string(err, &str);
699 throw HdfException("%s", str.buf);
703 ///////////////////////////////////////////////////////////////////////////////
704 // exceptions
706 HdfException::HdfException(const char *fmt, ...) {
707 va_list ap; va_start(ap, fmt); format(fmt, ap); va_end(ap);
710 ///////////////////////////////////////////////////////////////////////////////