1/*
2 * Copyright (C) 2012, 2013 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 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "ProfilerDatabase.h"
28
29#include "CodeBlock.h"
30#include "JSONObject.h"
31#include "ObjectConstructor.h"
32#include "Operations.h"
33
34namespace JSC { namespace Profiler {
35
36#if COMPILER(MINGW) || COMPILER(MSVC7_OR_LOWER) || OS(WINCE)
37static int databaseCounter;
38#else
39static volatile int databaseCounter;
40#endif
41static SpinLock registrationLock = SPINLOCK_INITIALIZER;
42static int didRegisterAtExit;
43static Database* firstDatabase;
44
45Database::Database(VM& vm)
46    : m_databaseID(atomicIncrement(&databaseCounter))
47    , m_vm(vm)
48    , m_shouldSaveAtExit(false)
49    , m_nextRegisteredDatabase(0)
50{
51}
52
53Database::~Database()
54{
55    if (m_shouldSaveAtExit) {
56        removeDatabaseFromAtExit();
57        performAtExitSave();
58    }
59}
60
61Bytecodes* Database::ensureBytecodesFor(CodeBlock* codeBlock)
62{
63    codeBlock = codeBlock->baselineVersion();
64
65    HashMap<CodeBlock*, Bytecodes*>::iterator iter = m_bytecodesMap.find(codeBlock);
66    if (iter != m_bytecodesMap.end())
67        return iter->value;
68
69    m_bytecodes.append(Bytecodes(m_bytecodes.size(), codeBlock));
70    Bytecodes* result = &m_bytecodes.last();
71
72    m_bytecodesMap.add(codeBlock, result);
73
74    return result;
75}
76
77void Database::notifyDestruction(CodeBlock* codeBlock)
78{
79    m_bytecodesMap.remove(codeBlock);
80}
81
82PassRefPtr<Compilation> Database::newCompilation(Bytecodes* bytecodes, CompilationKind kind)
83{
84    RefPtr<Compilation> compilation = adoptRef(new Compilation(bytecodes, kind));
85    m_compilations.append(compilation);
86    return compilation.release();
87}
88
89PassRefPtr<Compilation> Database::newCompilation(CodeBlock* codeBlock, CompilationKind kind)
90{
91    return newCompilation(ensureBytecodesFor(codeBlock), kind);
92}
93
94JSValue Database::toJS(ExecState* exec) const
95{
96    JSObject* result = constructEmptyObject(exec);
97
98    JSArray* bytecodes = constructEmptyArray(exec, 0);
99    for (unsigned i = 0; i < m_bytecodes.size(); ++i)
100        bytecodes->putDirectIndex(exec, i, m_bytecodes[i].toJS(exec));
101    result->putDirect(exec->vm(), exec->propertyNames().bytecodes, bytecodes);
102
103    JSArray* compilations = constructEmptyArray(exec, 0);
104    for (unsigned i = 0; i < m_compilations.size(); ++i)
105        compilations->putDirectIndex(exec, i, m_compilations[i]->toJS(exec));
106    result->putDirect(exec->vm(), exec->propertyNames().compilations, compilations);
107
108    return result;
109}
110
111String Database::toJSON() const
112{
113    JSGlobalObject* globalObject = JSGlobalObject::create(
114        m_vm, JSGlobalObject::createStructure(m_vm, jsNull()));
115
116    return JSONStringify(globalObject->globalExec(), toJS(globalObject->globalExec()), 0);
117}
118
119bool Database::save(const char* filename) const
120{
121    OwnPtr<FilePrintStream> out = FilePrintStream::open(filename, "w");
122    if (!out)
123        return false;
124
125    out->print(toJSON());
126    return true;
127}
128
129void Database::registerToSaveAtExit(const char* filename)
130{
131    m_atExitSaveFilename = filename;
132
133    if (m_shouldSaveAtExit)
134        return;
135
136    addDatabaseToAtExit();
137    m_shouldSaveAtExit = true;
138}
139
140void Database::addDatabaseToAtExit()
141{
142    if (atomicIncrement(&didRegisterAtExit) == 1)
143        atexit(atExitCallback);
144
145    TCMalloc_SpinLockHolder holder(&registrationLock);
146    m_nextRegisteredDatabase = firstDatabase;
147    firstDatabase = this;
148}
149
150void Database::removeDatabaseFromAtExit()
151{
152    TCMalloc_SpinLockHolder holder(&registrationLock);
153    for (Database** current = &firstDatabase; *current; current = &(*current)->m_nextRegisteredDatabase) {
154        if (*current != this)
155            continue;
156        *current = m_nextRegisteredDatabase;
157        m_nextRegisteredDatabase = 0;
158        m_shouldSaveAtExit = false;
159        break;
160    }
161}
162
163void Database::performAtExitSave() const
164{
165    save(m_atExitSaveFilename.data());
166}
167
168Database* Database::removeFirstAtExitDatabase()
169{
170    TCMalloc_SpinLockHolder holder(&registrationLock);
171    Database* result = firstDatabase;
172    if (result) {
173        firstDatabase = result->m_nextRegisteredDatabase;
174        result->m_nextRegisteredDatabase = 0;
175        result->m_shouldSaveAtExit = false;
176    }
177    return result;
178}
179
180void Database::atExitCallback()
181{
182    while (Database* database = removeFirstAtExitDatabase())
183        database->performAtExitSave();
184}
185
186} } // namespace JSC::Profiler
187
188