| 1 | /* | 
| 2 |  * Copyright (C) 2008 Apple Inc. All rights reserved. | 
| 3 |  * | 
| 4 |  * Redistribution and use in source and binary forms, with or without | 
| 5 |  * modification, are permitted provided that the following conditions | 
| 6 |  * are met: | 
| 7 |  * | 
| 8 |  * 1.  Redistributions of source code must retain the above copyright | 
| 9 |  *     notice, this list of conditions and the following disclaimer. | 
| 10 |  * 2.  Redistributions in binary form must reproduce the above copyright | 
| 11 |  *     notice, this list of conditions and the following disclaimer in the | 
| 12 |  *     documentation and/or other materials provided with the distribution. | 
| 13 |  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of | 
| 14 |  *     its contributors may be used to endorse or promote products derived | 
| 15 |  *     from this software without specific prior written permission. | 
| 16 |  * | 
| 17 |  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY | 
| 18 |  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | 
| 19 |  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | 
| 20 |  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY | 
| 21 |  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | 
| 22 |  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | 
| 23 |  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | 
| 24 |  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 
| 25 |  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | 
| 26 |  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 
| 27 |  */ | 
| 28 |  | 
| 29 | #include "config.h" | 
| 30 | #include "ProfileNode.h" | 
| 31 |  | 
| 32 | #include "Profiler.h" | 
| 33 | #include <stdio.h> | 
| 34 | #include <wtf/DateMath.h> | 
| 35 |  | 
| 36 | #if OS(WINDOWS) | 
| 37 | #include <windows.h> | 
| 38 | #endif | 
| 39 |  | 
| 40 | using namespace WTF; | 
| 41 |  | 
| 42 | namespace JSC { | 
| 43 |  | 
| 44 | static double getCount() | 
| 45 | { | 
| 46 | #if OS(WINDOWS) | 
| 47 |     static LARGE_INTEGER frequency = {0}; | 
| 48 |     if (!frequency.QuadPart) | 
| 49 |         QueryPerformanceFrequency(&frequency); | 
| 50 |     LARGE_INTEGER counter; | 
| 51 |     QueryPerformanceCounter(&counter); | 
| 52 |     return static_cast<double>(counter.QuadPart) / frequency.QuadPart; | 
| 53 | #else | 
| 54 |     return currentTimeMS(); | 
| 55 | #endif | 
| 56 | } | 
| 57 |  | 
| 58 | ProfileNode::ProfileNode(const CallIdentifier& callIdentifier, ProfileNode* headNode, ProfileNode* parentNode) | 
| 59 |     : m_callIdentifier(callIdentifier) | 
| 60 |     , m_head(headNode) | 
| 61 |     , m_parent(parentNode) | 
| 62 |     , m_nextSibling(0) | 
| 63 |     , m_startTime(0.0) | 
| 64 |     , m_actualTotalTime(0.0) | 
| 65 |     , m_visibleTotalTime(0.0) | 
| 66 |     , m_actualSelfTime(0.0) | 
| 67 |     , m_visibleSelfTime(0.0) | 
| 68 |     , m_numberOfCalls(0) | 
| 69 |     , m_visible(true) | 
| 70 | { | 
| 71 |     startTimer(); | 
| 72 | } | 
| 73 |  | 
| 74 | ProfileNode::ProfileNode(ProfileNode* headNode, ProfileNode* nodeToCopy) | 
| 75 |     : m_callIdentifier(nodeToCopy->callIdentifier()) | 
| 76 |     , m_head(headNode) | 
| 77 |     , m_parent(nodeToCopy->parent()) | 
| 78 |     , m_nextSibling(0) | 
| 79 |     , m_startTime(0.0) | 
| 80 |     , m_actualTotalTime(nodeToCopy->actualTotalTime()) | 
| 81 |     , m_visibleTotalTime(nodeToCopy->totalTime()) | 
| 82 |     , m_actualSelfTime(nodeToCopy->actualSelfTime()) | 
| 83 |     , m_visibleSelfTime(nodeToCopy->selfTime()) | 
| 84 |     , m_numberOfCalls(nodeToCopy->numberOfCalls()) | 
| 85 |     , m_visible(nodeToCopy->visible()) | 
| 86 | { | 
| 87 | } | 
| 88 |  | 
| 89 | ProfileNode* ProfileNode::willExecute(const CallIdentifier& callIdentifier) | 
| 90 | { | 
| 91 |     for (StackIterator currentChild = m_children.begin(); currentChild != m_children.end(); ++currentChild) { | 
| 92 |         if ((*currentChild)->callIdentifier() == callIdentifier) { | 
| 93 |             (*currentChild)->startTimer(); | 
| 94 |             return (*currentChild).get(); | 
| 95 |         } | 
| 96 |     } | 
| 97 |  | 
| 98 |     RefPtr<ProfileNode> newChild = ProfileNode::create(callIdentifier, headNode: m_head ? m_head : this, parentNode: this); // If this ProfileNode has no head it is the head. | 
| 99 |     if (m_children.size()) | 
| 100 |         m_children.last()->setNextSibling(newChild.get()); | 
| 101 |     m_children.append(val: newChild.release()); | 
| 102 |     return m_children.last().get(); | 
| 103 | } | 
| 104 |  | 
| 105 | ProfileNode* ProfileNode::didExecute() | 
| 106 | { | 
| 107 |     endAndRecordCall(); | 
| 108 |     return m_parent; | 
| 109 | } | 
| 110 |  | 
| 111 | void ProfileNode::addChild(PassRefPtr<ProfileNode> prpChild) | 
| 112 | { | 
| 113 |     RefPtr<ProfileNode> child = prpChild; | 
| 114 |     child->setParent(this); | 
| 115 |     if (m_children.size()) | 
| 116 |         m_children.last()->setNextSibling(child.get()); | 
| 117 |     m_children.append(val: child.release()); | 
| 118 | } | 
| 119 |  | 
| 120 | ProfileNode* ProfileNode::findChild(ProfileNode* node) const | 
| 121 | { | 
| 122 |     if (!node) | 
| 123 |         return 0; | 
| 124 |  | 
| 125 |     for (size_t i = 0; i < m_children.size(); ++i) { | 
| 126 |         if (*node == m_children[i].get()) | 
| 127 |             return m_children[i].get(); | 
| 128 |     } | 
| 129 |  | 
| 130 |     return 0; | 
| 131 | } | 
| 132 |  | 
| 133 | void ProfileNode::removeChild(ProfileNode* node) | 
| 134 | { | 
| 135 |     if (!node) | 
| 136 |         return; | 
| 137 |  | 
| 138 |     for (size_t i = 0; i < m_children.size(); ++i) { | 
| 139 |         if (*node == m_children[i].get()) { | 
| 140 |             m_children.remove(position: i); | 
| 141 |             break; | 
| 142 |         } | 
| 143 |     } | 
| 144 |      | 
| 145 |     resetChildrensSiblings(); | 
| 146 | } | 
| 147 |  | 
| 148 | void ProfileNode::insertNode(PassRefPtr<ProfileNode> prpNode) | 
| 149 | { | 
| 150 |     RefPtr<ProfileNode> node = prpNode; | 
| 151 |  | 
| 152 |     for (unsigned i = 0; i < m_children.size(); ++i) | 
| 153 |         node->addChild(prpChild: m_children[i].release()); | 
| 154 |  | 
| 155 |     m_children.clear(); | 
| 156 |     m_children.append(val: node.release()); | 
| 157 | } | 
| 158 |  | 
| 159 | void ProfileNode::stopProfiling() | 
| 160 | { | 
| 161 |     if (m_startTime) | 
| 162 |         endAndRecordCall(); | 
| 163 |  | 
| 164 |     m_visibleTotalTime = m_actualTotalTime; | 
| 165 |  | 
| 166 |     ASSERT(m_actualSelfTime == 0.0 && m_startTime == 0.0); | 
| 167 |  | 
| 168 |     // Because we iterate in post order all of our children have been stopped before us. | 
| 169 |     for (unsigned i = 0; i < m_children.size(); ++i) | 
| 170 |         m_actualSelfTime += m_children[i]->totalTime(); | 
| 171 |  | 
| 172 |     ASSERT(m_actualSelfTime <= m_actualTotalTime); | 
| 173 |     m_actualSelfTime = m_actualTotalTime - m_actualSelfTime; | 
| 174 |     m_visibleSelfTime = m_actualSelfTime; | 
| 175 | } | 
| 176 |  | 
| 177 | ProfileNode* ProfileNode::traverseNextNodePostOrder() const | 
| 178 | { | 
| 179 |     ProfileNode* next = m_nextSibling; | 
| 180 |     if (!next) | 
| 181 |         return m_parent; | 
| 182 |     while (ProfileNode* firstChild = next->firstChild()) | 
| 183 |         next = firstChild; | 
| 184 |     return next; | 
| 185 | } | 
| 186 |  | 
| 187 | ProfileNode* ProfileNode::traverseNextNodePreOrder(bool processChildren) const | 
| 188 | { | 
| 189 |     if (processChildren && m_children.size()) | 
| 190 |         return m_children[0].get(); | 
| 191 |  | 
| 192 |     if (m_nextSibling) | 
| 193 |         return m_nextSibling; | 
| 194 |  | 
| 195 |     ProfileNode* nextParent = m_parent; | 
| 196 |     if (!nextParent) | 
| 197 |         return 0; | 
| 198 |  | 
| 199 |     ProfileNode* next; | 
| 200 |     for (next = m_parent->nextSibling(); !next; next = nextParent->nextSibling()) { | 
| 201 |         nextParent = nextParent->parent(); | 
| 202 |         if (!nextParent) | 
| 203 |             return 0; | 
| 204 |     } | 
| 205 |  | 
| 206 |     return next; | 
| 207 | } | 
| 208 |  | 
| 209 | void ProfileNode::setTreeVisible(ProfileNode* node, bool visible) | 
| 210 | { | 
| 211 |     ProfileNode* nodeParent = node->parent(); | 
| 212 |     ProfileNode* nodeSibling = node->nextSibling(); | 
| 213 |     node->setParent(0); | 
| 214 |     node->setNextSibling(0); | 
| 215 |  | 
| 216 |     for (ProfileNode* currentNode = node; currentNode; currentNode = currentNode->traverseNextNodePreOrder()) | 
| 217 |         currentNode->setVisible(visible); | 
| 218 |  | 
| 219 |     node->setParent(nodeParent); | 
| 220 |     node->setNextSibling(nodeSibling); | 
| 221 | } | 
| 222 |  | 
| 223 | void ProfileNode::calculateVisibleTotalTime() | 
| 224 | { | 
| 225 |     double sumOfVisibleChildrensTime = 0.0; | 
| 226 |  | 
| 227 |     for (unsigned i = 0; i < m_children.size(); ++i) { | 
| 228 |         if (m_children[i]->visible()) | 
| 229 |             sumOfVisibleChildrensTime += m_children[i]->totalTime(); | 
| 230 |     } | 
| 231 |  | 
| 232 |     m_visibleTotalTime = m_visibleSelfTime + sumOfVisibleChildrensTime; | 
| 233 | } | 
| 234 |  | 
| 235 | bool ProfileNode::focus(const CallIdentifier& callIdentifier) | 
| 236 | { | 
| 237 |     if (!m_visible) | 
| 238 |         return false; | 
| 239 |  | 
| 240 |     if (m_callIdentifier != callIdentifier) { | 
| 241 |         m_visible = false; | 
| 242 |         return true; | 
| 243 |     } | 
| 244 |  | 
| 245 |     for (ProfileNode* currentParent = m_parent; currentParent; currentParent = currentParent->parent()) | 
| 246 |         currentParent->setVisible(true); | 
| 247 |  | 
| 248 |     return false; | 
| 249 | } | 
| 250 |  | 
| 251 | void ProfileNode::exclude(const CallIdentifier& callIdentifier) | 
| 252 | { | 
| 253 |     if (m_visible && m_callIdentifier == callIdentifier) { | 
| 254 |         setTreeVisible(node: this, visible: false); | 
| 255 |  | 
| 256 |         m_parent->setVisibleSelfTime(m_parent->selfTime() + m_visibleTotalTime); | 
| 257 |     } | 
| 258 | } | 
| 259 |  | 
| 260 | void ProfileNode::restore() | 
| 261 | { | 
| 262 |     m_visibleTotalTime = m_actualTotalTime; | 
| 263 |     m_visibleSelfTime = m_actualSelfTime; | 
| 264 |     m_visible = true; | 
| 265 | } | 
| 266 |  | 
| 267 | void ProfileNode::endAndRecordCall() | 
| 268 | { | 
| 269 |     m_actualTotalTime += m_startTime ? getCount() - m_startTime : 0.0; | 
| 270 |     m_startTime = 0.0; | 
| 271 |  | 
| 272 |     ++m_numberOfCalls; | 
| 273 | } | 
| 274 |  | 
| 275 | void ProfileNode::startTimer() | 
| 276 | { | 
| 277 |     if (!m_startTime) | 
| 278 |         m_startTime = getCount(); | 
| 279 | } | 
| 280 |  | 
| 281 | void ProfileNode::resetChildrensSiblings() | 
| 282 | { | 
| 283 |     unsigned size = m_children.size(); | 
| 284 |     for (unsigned i = 0; i < size; ++i) | 
| 285 |         m_children[i]->setNextSibling(i + 1 == size ? 0 : m_children[i + 1].get()); | 
| 286 | } | 
| 287 |  | 
| 288 | #ifndef NDEBUG | 
| 289 | void ProfileNode::debugPrintData(int indentLevel) const | 
| 290 | { | 
| 291 |     // Print function names | 
| 292 |     for (int i = 0; i < indentLevel; ++i) | 
| 293 |         printf(format: "  " ); | 
| 294 |  | 
| 295 |     printf(format: "Function Name %s %d SelfTime %.3fms/%.3f%% TotalTime %.3fms/%.3f%% VSelf %.3fms VTotal %.3fms Visible %s Next Sibling %s\n" , | 
| 296 |         functionName().UTF8String().c_str(),  | 
| 297 |         m_numberOfCalls, m_actualSelfTime, selfPercent(), m_actualTotalTime, totalPercent(), | 
| 298 |         m_visibleSelfTime, m_visibleTotalTime,  | 
| 299 |         (m_visible ? "True"  : "False" ), | 
| 300 |         m_nextSibling ? m_nextSibling->functionName().UTF8String().c_str() : "" ); | 
| 301 |  | 
| 302 |     ++indentLevel; | 
| 303 |  | 
| 304 |     // Print children's names and information | 
| 305 |     for (StackIterator currentChild = m_children.begin(); currentChild != m_children.end(); ++currentChild) | 
| 306 |         (*currentChild)->debugPrintData(indentLevel); | 
| 307 | } | 
| 308 |  | 
| 309 | // print the profiled data in a format that matches the tool sample's output. | 
| 310 | double ProfileNode::debugPrintDataSampleStyle(int indentLevel, FunctionCallHashCount& countedFunctions) const | 
| 311 | { | 
| 312 |     printf(format: "    " ); | 
| 313 |  | 
| 314 |     // Print function names | 
| 315 |     const char* name = functionName().UTF8String().c_str(); | 
| 316 |     double sampleCount = m_actualTotalTime * 1000; | 
| 317 |     if (indentLevel) { | 
| 318 |         for (int i = 0; i < indentLevel; ++i) | 
| 319 |             printf(format: "  " ); | 
| 320 |  | 
| 321 |          countedFunctions.add(value: functionName().rep()); | 
| 322 |  | 
| 323 |         printf(format: "%.0f %s\n" , sampleCount ? sampleCount : 1, name); | 
| 324 |     } else | 
| 325 |         printf(format: "%s\n" , name); | 
| 326 |  | 
| 327 |     ++indentLevel; | 
| 328 |  | 
| 329 |     // Print children's names and information | 
| 330 |     double sumOfChildrensCount = 0.0; | 
| 331 |     for (StackIterator currentChild = m_children.begin(); currentChild != m_children.end(); ++currentChild) | 
| 332 |         sumOfChildrensCount += (*currentChild)->debugPrintDataSampleStyle(indentLevel, countedFunctions); | 
| 333 |  | 
| 334 |     sumOfChildrensCount *= 1000;    // | 
| 335 |     // Print remainder of samples to match sample's output | 
| 336 |     if (sumOfChildrensCount < sampleCount) { | 
| 337 |         printf(format: "    " ); | 
| 338 |         while (indentLevel--) | 
| 339 |             printf(format: "  " ); | 
| 340 |  | 
| 341 |         printf(format: "%.0f %s\n" , sampleCount - sumOfChildrensCount, functionName().UTF8String().c_str()); | 
| 342 |     } | 
| 343 |  | 
| 344 |     return m_actualTotalTime; | 
| 345 | } | 
| 346 | #endif | 
| 347 |  | 
| 348 | } // namespace JSC | 
| 349 |  |