1/*
2 * Copyright (C) 2008, 2014 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 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 "LegacyProfiler.h"
33#include <wtf/DateMath.h>
34#include <wtf/DataLog.h>
35#include <wtf/text/StringHash.h>
36
37using namespace WTF;
38
39namespace JSC {
40
41ProfileNode::ProfileNode(ExecState* callerCallFrame, const CallIdentifier& callIdentifier, ProfileNode* headNode, ProfileNode* parentNode)
42    : m_callerCallFrame(callerCallFrame)
43    , m_callIdentifier(callIdentifier)
44    , m_head(headNode)
45    , m_parent(parentNode)
46    , m_nextSibling(nullptr)
47    , m_totalTime(0)
48    , m_selfTime(0)
49{
50    startTimer();
51}
52
53ProfileNode::ProfileNode(ExecState* callerCallFrame, ProfileNode* headNode, ProfileNode* nodeToCopy)
54    : m_callerCallFrame(callerCallFrame)
55    , m_callIdentifier(nodeToCopy->callIdentifier())
56    , m_head(headNode)
57    , m_parent(nodeToCopy->parent())
58    , m_nextSibling(0)
59    , m_totalTime(nodeToCopy->totalTime())
60    , m_selfTime(nodeToCopy->selfTime())
61    , m_calls(nodeToCopy->calls())
62{
63}
64
65ProfileNode* ProfileNode::willExecute(ExecState* callerCallFrame, const CallIdentifier& callIdentifier)
66{
67    for (StackIterator currentChild = m_children.begin(); currentChild != m_children.end(); ++currentChild) {
68        if ((*currentChild)->callIdentifier() == callIdentifier) {
69            (*currentChild)->startTimer();
70            return (*currentChild).get();
71        }
72    }
73
74    RefPtr<ProfileNode> newChild = ProfileNode::create(callerCallFrame, callIdentifier, m_head ? m_head : this, this); // If this ProfileNode has no head it is the head.
75    if (m_children.size())
76        m_children.last()->setNextSibling(newChild.get());
77    m_children.append(newChild.release());
78    return m_children.last().get();
79}
80
81ProfileNode* ProfileNode::didExecute()
82{
83    endAndRecordCall();
84    return m_parent;
85}
86
87void ProfileNode::addChild(PassRefPtr<ProfileNode> prpChild)
88{
89    RefPtr<ProfileNode> child = prpChild;
90    child->setParent(this);
91    if (m_children.size())
92        m_children.last()->setNextSibling(child.get());
93    m_children.append(child.release());
94}
95
96void ProfileNode::removeChild(ProfileNode* node)
97{
98    if (!node)
99        return;
100
101    for (size_t i = 0; i < m_children.size(); ++i) {
102        if (*node == m_children[i].get()) {
103            m_children.remove(i);
104            break;
105        }
106    }
107
108    resetChildrensSiblings();
109}
110
111void ProfileNode::insertNode(PassRefPtr<ProfileNode> prpNode)
112{
113    RefPtr<ProfileNode> node = prpNode;
114
115    for (unsigned i = 0; i < m_children.size(); ++i)
116        node->addChild(m_children[i].release());
117
118    m_children.clear();
119    m_children.append(node.release());
120}
121
122void ProfileNode::stopProfiling()
123{
124    ASSERT(!m_calls.isEmpty());
125
126    if (isnan(m_calls.last().totalTime()))
127        endAndRecordCall();
128
129    // Because we iterate in post order all of our children have been stopped before us.
130    for (unsigned i = 0; i < m_children.size(); ++i)
131        m_selfTime += m_children[i]->totalTime();
132
133    ASSERT(m_selfTime <= m_totalTime);
134    m_selfTime = m_totalTime - m_selfTime;
135}
136
137ProfileNode* ProfileNode::traverseNextNodePostOrder() const
138{
139    ProfileNode* next = m_nextSibling;
140    if (!next)
141        return m_parent;
142    while (ProfileNode* firstChild = next->firstChild())
143        next = firstChild;
144    return next;
145}
146
147void ProfileNode::endAndRecordCall()
148{
149    Call& last = lastCall();
150    ASSERT(isnan(last.totalTime()));
151
152    last.setTotalTime(currentTime() - last.startTime());
153
154    m_totalTime += last.totalTime();
155}
156
157void ProfileNode::startTimer()
158{
159    m_calls.append(Call(currentTime()));
160}
161
162void ProfileNode::resetChildrensSiblings()
163{
164    unsigned size = m_children.size();
165    for (unsigned i = 0; i < size; ++i)
166        m_children[i]->setNextSibling(i + 1 == size ? 0 : m_children[i + 1].get());
167}
168
169#ifndef NDEBUG
170void ProfileNode::debugPrintData(int indentLevel) const
171{
172    // Print function names
173    for (int i = 0; i < indentLevel; ++i)
174        dataLogF("  ");
175
176    dataLogF("Function Name %s %zu SelfTime %.3fms/%.3f%% TotalTime %.3fms/%.3f%% Next Sibling %s\n",
177        functionName().utf8().data(),
178        numberOfCalls(), m_selfTime, selfPercent(), m_totalTime, totalPercent(),
179        m_nextSibling ? m_nextSibling->functionName().utf8().data() : "");
180
181    ++indentLevel;
182
183    // Print children's names and information
184    for (StackIterator currentChild = m_children.begin(); currentChild != m_children.end(); ++currentChild)
185        (*currentChild)->debugPrintData(indentLevel);
186}
187
188// print the profiled data in a format that matches the tool sample's output.
189double ProfileNode::debugPrintDataSampleStyle(int indentLevel, FunctionCallHashCount& countedFunctions) const
190{
191    dataLogF("    ");
192
193    // Print function names
194    const char* name = functionName().utf8().data();
195    double sampleCount = m_totalTime * 1000;
196    if (indentLevel) {
197        for (int i = 0; i < indentLevel; ++i)
198            dataLogF("  ");
199
200         countedFunctions.add(functionName().impl());
201
202        dataLogF("%.0f %s\n", sampleCount ? sampleCount : 1, name);
203    } else
204        dataLogF("%s\n", name);
205
206    ++indentLevel;
207
208    // Print children's names and information
209    double sumOfChildrensCount = 0.0;
210    for (StackIterator currentChild = m_children.begin(); currentChild != m_children.end(); ++currentChild)
211        sumOfChildrensCount += (*currentChild)->debugPrintDataSampleStyle(indentLevel, countedFunctions);
212
213    sumOfChildrensCount *= 1000;    //
214    // Print remainder of samples to match sample's output
215    if (sumOfChildrensCount < sampleCount) {
216        dataLogF("    ");
217        while (indentLevel--)
218            dataLogF("  ");
219
220        dataLogF("%.0f %s\n", sampleCount - sumOfChildrensCount, functionName().utf8().data());
221    }
222
223    return m_totalTime;
224}
225#endif
226
227} // namespace JSC
228