1/*
2 * Copyright (C) 2013 Google 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 *
14 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
18 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
21 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "InstanceCounter.h"
28
29#include "wtf/HashMap.h"
30#include "wtf/StdLibExtras.h"
31#include "wtf/ThreadingPrimitives.h"
32#include "wtf/text/StringBuilder.h"
33#include "wtf/text/StringHash.h"
34#include "wtf/text/WTFString.h"
35
36namespace WTF {
37
38#if ENABLE(INSTANCE_COUNTER) || ENABLE(GC_TRACING)
39
40#if COMPILER(CLANG)
41const size_t extractNameFunctionPrefixLength = sizeof("const char *WTF::extractNameFunction() [T = ") - 1;
42const size_t extractNameFunctionPostfixLength = 1;
43#elif COMPILER(GCC)
44const size_t extractNameFunctionPrefixLength = sizeof("const char* WTF::extractNameFunction() [with T = ") - 1;
45const size_t extractNameFunctionPostfixLength = 1;
46#else
47#warning "Extracting typename in a compiler other than GCC isn't supported atm"
48#endif
49
50// This function is used to stringify a typename T without using RTTI.
51// The result of extractNameFunction<T>() is given as |funcName|. |extractTypeNameFromFunctionName| then extracts a typename string from |funcName|.
52String extractTypeNameFromFunctionName(const char* funcName)
53{
54#if COMPILER(CLANG) || COMPILER(GCC)
55    size_t funcNameLength = strlen(funcName);
56    ASSERT(funcNameLength > extractNameFunctionPrefixLength + 1);
57
58    const char* funcNameWithoutPrefix = funcName + extractNameFunctionPrefixLength;
59    return String(funcNameWithoutPrefix, funcNameLength - extractNameFunctionPrefixLength - extractNameFunctionPostfixLength /* last ] */);
60#else
61    return String();
62#endif
63}
64
65class InstanceCounter {
66public:
67    void incrementInstanceCount(const String& instanceName, void* ptr);
68    void decrementInstanceCount(const String& instanceName, void* ptr);
69    String dump();
70
71    static InstanceCounter* instance()
72    {
73        DEFINE_STATIC_LOCAL(InstanceCounter, self, ());
74        return &self;
75    }
76
77private:
78    InstanceCounter() { }
79
80    Mutex m_mutex;
81    HashMap<String, int> m_counterMap;
82};
83
84void incrementInstanceCount(const char* extractNameFunctionName, void* ptr)
85{
86    String instanceName = extractTypeNameFromFunctionName(extractNameFunctionName);
87    InstanceCounter::instance()->incrementInstanceCount(instanceName, ptr);
88}
89
90void decrementInstanceCount(const char* extractNameFunctionName, void* ptr)
91{
92    String instanceName = extractTypeNameFromFunctionName(extractNameFunctionName);
93    InstanceCounter::instance()->decrementInstanceCount(instanceName, ptr);
94}
95
96String dumpRefCountedInstanceCounts()
97{
98    return InstanceCounter::instance()->dump();
99}
100
101void InstanceCounter::incrementInstanceCount(const String& instanceName, void* ptr)
102{
103    MutexLocker locker(m_mutex);
104    HashMap<String, int>::AddResult result = m_counterMap.add(instanceName, 1);
105    if (!result.isNewEntry)
106        ++(result.storedValue->value);
107}
108
109void InstanceCounter::decrementInstanceCount(const String& instanceName, void* ptr)
110{
111    MutexLocker locker(m_mutex);
112    HashMap<String, int>::iterator it = m_counterMap.find(instanceName);
113    ASSERT(it != m_counterMap.end());
114
115    --(it->value);
116    if (!it->value)
117        m_counterMap.remove(it);
118}
119
120String InstanceCounter::dump()
121{
122    MutexLocker locker(m_mutex);
123
124    StringBuilder builder;
125
126    builder.append("{");
127    HashMap<String, int>::iterator it = m_counterMap.begin();
128    HashMap<String, int>::iterator itEnd = m_counterMap.end();
129    for (; it != itEnd; ++it) {
130        if (it != m_counterMap.begin())
131            builder.append(",");
132        builder.append("\"");
133        builder.append(it->key);
134        builder.append("\": ");
135        builder.append(String::number(it->value));
136    }
137    builder.append("}");
138
139    return builder.toString();
140}
141
142#else
143
144String dumpRefCountedInstanceCounts()
145{
146    return String("{}");
147}
148
149#endif // ENABLE(INSTANCE_COUNTER) || ENABLE(GC_TRACING)
150
151} // namespace WTF
152