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(®istrationLock); 146 m_nextRegisteredDatabase = firstDatabase; 147 firstDatabase = this; 148} 149 150void Database::removeDatabaseFromAtExit() 151{ 152 TCMalloc_SpinLockHolder holder(®istrationLock); 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(®istrationLock); 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