SetStr and AddStr methods on a Vector do the same thing.
[hiphop-php.git] / hphp / runtime / base / profile_dump.cpp
blobdb5823ce278a95077b326c5806b632c86d05ab12
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2013 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/runtime/base/profile_dump.h"
19 #include "folly/Format.h"
20 #include "folly/Conv.h"
21 #include "hphp/runtime/base/execution_context.h"
23 namespace HPHP {
25 std::string ProfileDump::toPProfFormat() const {
26 size_t currentCountSum = 0, currentBytesSum = 0;
27 size_t accumCountSum = 0, accumBytesSum = 0;
29 int numDumps = std::max(m_numDumps, 1);
31 // aggregate for totals at top
32 for (const auto &current : m_currentlyAllocated) {
33 currentCountSum += current.second.m_count;
34 currentBytesSum += current.second.m_bytes;
36 for (const auto &accum : m_accumAllocated) {
37 accumCountSum += accum.second.m_count;
38 accumBytesSum += accum.second.m_bytes;
41 std::string res;
42 // print header, with the following format
43 // heap profile: curobjs: curbytes [accumobjs: accumbytes] @ heapprofile
44 folly::toAppend(
45 folly::format(
46 "heap profile: {}: {} [{}: {}] @ heapprofile\n",
47 currentCountSum, currentBytesSum, accumCountSum, accumBytesSum
48 ).str(), &res
51 // print information for allocations we have recorded for each call stack
52 // each line has the following format
53 // curobjs: curbytes [accumobjs: accumbytes] stacktrace...
54 for (const auto &accum : m_accumAllocated) {
55 const auto &trace = accum.first;
56 // skip this information if we have a zero-length stack trace, because
57 // that means we allocated this outside of PHP userland.
58 if (trace.size() == 0) continue;
59 // if the filters are set such that the number of bytes or allocations
60 // at this site is not enough, we are going to filter it out
61 if ((accum.second.m_count / numDumps <
62 RuntimeOption::HHProfServerFilterMinAllocPerReq) ||
63 (accum.second.m_bytes / numDumps <
64 RuntimeOption::HHProfServerFilterMinBytesPerReq)) continue;
65 // get current allocation count/bytes for the current stack trace. we
66 // know this is present in the current allocations map because we did
67 // insert it at the same time we inserted something into the cumulative
68 // allocations map
69 const auto &it = m_currentlyAllocated.find(trace);
70 assert(it != m_currentlyAllocated.end());
71 // dump current and cumulative count/bytes
72 folly::toAppend(
73 folly::format(
74 "{}: {} [{}: {}] @",
75 it->second.m_count, it->second.m_bytes,
76 accum.second.m_count, accum.second.m_bytes
77 ).str(), &res
79 // walk stack trace and pack srckeys into 64-bit ints so
80 // they look like addresses
81 for (const auto &sk : trace) {
82 folly::toAppend(folly::format(" {:#x}", sk.toAtomicInt()), &res);
84 folly::toAppend("\n", &res);
87 // dump maps from /proc/pid/maps. we aren't going to use them because
88 // we are going to manually resolve symbols ourselves later, and the
89 // addresses we are dumping as part of the stack trace aren't even real
90 // addresses anyway
91 size_t buflen = 64;
92 folly::toAppend("\nMAPPED_LIBRARIES:\n", &res);
93 char buf[buflen];
94 snprintf(buf, buflen, "/proc/%d/maps", getpid());
95 FILE *f = fopen(buf, "r");
96 size_t bytesRead;
97 while ((bytesRead = fread(buf, 1, buflen, f)) > 0) {
98 folly::toAppend(folly::StringPiece(buf, bytesRead), &res);
100 fclose(f);
101 return res;
104 // ProfileController state
105 namespace {
106 // type of current request
107 // Next: profile the next request
108 // NextURL: profile the next request to m_url
109 // Global: profile all requests until stopped
110 enum RequestType {
111 None,
112 Next,
113 NextURL,
114 Global
115 } m_reqType;
117 // url to profile for NextURL type requests
118 std::string m_url;
120 // currently-held dump, valid if we are complete
121 ProfileDump m_dump;
123 // state of the controller
124 // Waiting: no request is active
125 // Pending: a request is active, but has not yet
126 // been fulfilled
127 // Complete: a request is active, and has been
128 // fulfilled
129 enum State {
130 Waiting,
131 Pending,
132 Complete
133 } m_state;
135 // synchronization primitives
136 std::condition_variable m_waitq;
137 std::mutex m_mutex;
141 // static
142 bool ProfileController::requestNext() {
143 std::unique_lock<std::mutex> lock(m_mutex);
145 // don't clobber another request!
146 if (m_state != State::Waiting) return false;
147 // we have the mutex and no other request is pending, we can
148 // place the request
149 m_reqType = RequestType::Next;
150 m_state = State::Pending;
152 return true;
155 // static
156 bool ProfileController::requestNextURL(const std::string &url) {
157 std::unique_lock<std::mutex> lock(m_mutex);
159 if (m_state != State::Waiting) return false;
160 m_reqType = RequestType::NextURL;
161 m_url = url;
162 m_state = State::Pending;
164 return true;
167 // static
168 bool ProfileController::requestGlobal() {
169 std::unique_lock<std::mutex> lock(m_mutex);
171 if (m_state != State::Waiting) return false;
172 m_reqType = RequestType::Global;
173 m_state = State::Pending;
175 // clean up the dump since we are going to copy in data
176 // from other dumps when they offer theirs
177 m_dump.clear();
179 return true;
182 // static
183 void ProfileController::cancelRequest() {
184 std::unique_lock<std::mutex> lock(m_mutex);
186 // changing the state is enough to cancel the currently
187 // active request; no VM thread will put their dump here
188 // if the state is waiting
189 m_state = State::Waiting;
190 m_waitq.notify_all();
193 // static
194 void ProfileController::offerProfile(const ProfileDump &dump) {
195 std::unique_lock<std::mutex> lock(m_mutex);
197 // we have to be waiting for a profile, or it has to be
198 // a global profile (since we merge many profiles into
199 // a global request)
200 if (m_state != State::Pending &&
201 !(m_reqType == RequestType::Global &&
202 m_state == State::Complete)) {
203 return;
206 // check if we are fulfilling the pending request
207 // 1. if the type is Next, we always fill it
208 // 2. if the type is NextURL, we fill it if the
209 // URL matches the one the VM thread is running
210 // 3. if the type is Global, we add our dump info
211 // to the dump in the controller
212 switch (m_reqType) {
213 case RequestType::Next:
214 m_dump = dump;
215 break;
216 case RequestType::NextURL:
217 if (g_context->getTransport()->getCommand() != m_url) return;
218 m_dump = dump;
219 break;
220 case RequestType::Global:
221 m_dump += dump;
222 break;
223 default:
224 not_reached();
226 m_state = State::Complete;
227 m_waitq.notify_all();
230 // static
231 ProfileDump ProfileController::waitForProfile() {
232 std::unique_lock<std::mutex> lock(m_mutex);
234 auto cond = [&] { return m_state != State::Pending; };
235 m_waitq.wait_for(
236 lock,
237 std::chrono::seconds(RuntimeOption::HHProfServerTimeoutSeconds),
238 cond
241 // check to see if someone else grabbed the profile
242 if (m_state == State::Waiting) return ProfileDump();
243 // otherwise, the profile is ours. reset the state and return it
244 m_state = State::Waiting;
245 m_reqType = RequestType::None;
246 return m_dump;