2 +----------------------------------------------------------------------+
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"
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 ¤t
: 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
;
42 // print header, with the following format
43 // heap profile: curobjs: curbytes [accumobjs: accumbytes] @ heapprofile
46 "heap profile: {}: {} [{}: {}] @ heapprofile\n",
47 currentCountSum
, currentBytesSum
, accumCountSum
, accumBytesSum
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
69 const auto &it
= m_currentlyAllocated
.find(trace
);
70 assert(it
!= m_currentlyAllocated
.end());
71 // dump current and cumulative count/bytes
75 it
->second
.m_count
, it
->second
.m_bytes
,
76 accum
.second
.m_count
, accum
.second
.m_bytes
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
92 folly::toAppend("\nMAPPED_LIBRARIES:\n", &res
);
94 snprintf(buf
, buflen
, "/proc/%d/maps", getpid());
95 FILE *f
= fopen(buf
, "r");
97 while ((bytesRead
= fread(buf
, 1, buflen
, f
)) > 0) {
98 folly::toAppend(folly::StringPiece(buf
, bytesRead
), &res
);
104 // ProfileController state
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
117 // url to profile for NextURL type requests
120 // currently-held dump, valid if we are complete
123 // state of the controller
124 // Waiting: no request is active
125 // Pending: a request is active, but has not yet
127 // Complete: a request is active, and has been
135 // synchronization primitives
136 std::condition_variable m_waitq
;
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
149 m_reqType
= RequestType::Next
;
150 m_state
= State::Pending
;
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
;
162 m_state
= State::Pending
;
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
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();
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
200 if (m_state
!= State::Pending
&&
201 !(m_reqType
== RequestType::Global
&&
202 m_state
== State::Complete
)) {
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
213 case RequestType::Next
:
216 case RequestType::NextURL
:
217 if (g_context
->getTransport()->getCommand() != m_url
) return;
220 case RequestType::Global
:
226 m_state
= State::Complete
;
227 m_waitq
.notify_all();
231 ProfileDump
ProfileController::waitForProfile() {
232 std::unique_lock
<std::mutex
> lock(m_mutex
);
234 auto cond
= [&] { return m_state
!= State::Pending
; };
237 std::chrono::seconds(RuntimeOption::HHProfServerTimeoutSeconds
),
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
;