blob: 20be563fd9bc78508b06e74b6091040283d42696 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @author Jack Liu, Mikhail Y. Fursov, Chen-Dong Yuan
*/
#include "EdgeProfileCollector.h"
#define LOG_DOMAIN "em"
#include "cxxlog.h"
#include <algorithm>
#include <assert.h>
#include <sstream>
#include "open/vm_method_access.h"
#include "open/vm_class_manipulation.h"
#include "jit_intf.h"
#include "port_mutex.h"
Method_Profile_Handle edge_profiler_create_profile( PC_Handle ph,
Method_Handle mh,
U_32 numCounters,
U_32* counterKeys,
U_32 checkSum )
{
ProfileCollector* pc = (ProfileCollector*)ph;
assert(pc->type == EM_PCTYPE_EDGE);
EdgeMethodProfile* profile =
((EdgeProfileCollector*)pc)->createProfile(mh, numCounters, counterKeys, checkSum);
return (Method_Profile_Handle)profile;
}
void* edge_profiler_get_entry_counter_addr(Method_Profile_Handle mph) {
MethodProfile* mp = (MethodProfile*)mph;
assert(mp->pc->type == EM_PCTYPE_EDGE);
EdgeMethodProfile* emp = (EdgeMethodProfile*)mp;
return &emp->entryCounter;
}
void* edge_profiler_get_counter_addr(Method_Profile_Handle mph, U_32 key)
{
MethodProfile* mp = (MethodProfile*)mph;
assert(mp->pc->type == EM_PCTYPE_EDGE);
return ((EdgeMethodProfile*)mp)->getCounter( key );
}
U_32 edge_profiler_get_num_counters(Method_Profile_Handle mph)
{
MethodProfile* mp = (MethodProfile*)mph;
assert(mp->pc->type == EM_PCTYPE_EDGE);
return (U_32)((EdgeMethodProfile*)mp)->counters.size();
}
U_32 edge_profiler_get_checksum(Method_Profile_Handle mph)
{
MethodProfile* mp = (MethodProfile*)mph;
assert(mp->pc->type == EM_PCTYPE_EDGE);
return ((EdgeMethodProfile*)mp)->checkSum;
}
U_32 edge_profiler_get_entry_threshold(PC_Handle pch) {
assert(pch!=NULL);
ProfileCollector* pc = (ProfileCollector*)pch;
assert(pc->type == EM_PCTYPE_EDGE);
return ((EdgeProfileCollector*)pc)->getEntryThreshold();
}
U_32 edge_profiler_get_backedge_threshold(PC_Handle pch) {
assert(pch!=NULL);
ProfileCollector* pc = (ProfileCollector*)pch;
assert(pc->type == EM_PCTYPE_EDGE);
return ((EdgeProfileCollector*)pc)->getBackedgeThreshold();
}
EdgeProfileCollector::EdgeProfileCollector(EM_PC_Interface* em, const std::string& name, JIT_Handle genJit,
U_32 _initialTimeout, U_32 _timeout,
U_32 _eThreshold, U_32 _bThreshold)
: ProfileCollector(em, name, EM_PCTYPE_EDGE, genJit), initialTimeout(_initialTimeout),
timeout(_timeout),eThreshold(_eThreshold), bThreshold(_bThreshold)
{
port_mutex_create(&profilesLock, APR_THREAD_MUTEX_NESTED);
catName = std::string(LOG_DOMAIN) + ".profiler." + name;
loggingEnabled = log_is_info_enabled(LOG_DOMAIN) || log_is_info_enabled(catName.c_str());
if (loggingEnabled) {
std::ostringstream msg;
msg<< "EM: edge profiler intialized: "<<name
<<" entry threshold:"<<eThreshold << " edge threshold:"<<bThreshold;
INFO2(catName.c_str(), msg.str().c_str());
}
}
EdgeProfileCollector::~EdgeProfileCollector()
{
EdgeProfilesMap::iterator it;
for( it = profilesByMethod.begin(); it != profilesByMethod.end(); it++ ){
EdgeMethodProfile* profile = it->second;
delete profile;
}
port_mutex_destroy(&profilesLock);
}
MethodProfile* EdgeProfileCollector::getMethodProfile(Method_Handle mh) const
{
port_mutex_lock(&profilesLock);
MethodProfile* res = NULL;
EdgeProfilesMap::const_iterator it = profilesByMethod.find(mh);
if (it != profilesByMethod.end()) {
res = it->second;
}
port_mutex_unlock(&profilesLock);
return res;
}
U_32* EdgeMethodProfile::getCounter( U_32 key ) const
{
//log2 search
EdgeMap::const_iterator it = lower_bound(cntMap.begin(), cntMap.end(), key);
if (it == cntMap.end() || *it != key) {
return NULL;
}
U_32 idx = (U_32)(it - cntMap.begin());
return (U_32*)&counters.front() + idx;
}
void EdgeMethodProfile::dump( const char* banner )
{
const char* methodName = method_get_name(mh);
Class_Handle ch = method_get_class(mh);
const char* className = class_get_name(ch);
const char* signature = method_get_descriptor(mh);
U_32 backEdgeCounter = entryCounter;
U_32 instrCost = entryCounter;
assert( banner != NULL );
fprintf( stderr, "%s: %s::%s%s\n", banner, className, methodName, signature );
for( U_32 i = 0; i < counters.size(); i++ ){
instrCost += counters[i];
if( counters[i] > backEdgeCounter ){
backEdgeCounter = counters[i];
}
}
assert( instrCost >= backEdgeCounter );
fprintf( stderr, "\t%s entry: %d\tcounters: %d\tbackedge: %d\tcost: %u\n",
_isHot ? "hot" : "cold",
entryCounter, counters.size(), backEdgeCounter, instrCost );
return;
}
EdgeMethodProfile* EdgeProfileCollector::createProfile( Method_Handle mh,
U_32 numCounters,
U_32* counterKeys,
U_32 checkSum)
{
port_mutex_lock(&profilesLock);
EdgeMethodProfile* profile = new EdgeMethodProfile(this, mh);
// Allocate space for edge counters.
assert( profile->cntMap.empty() );
profile->counters.resize(numCounters);
profile->checkSum = checkSum;
profile->cntMap.insert(profile->cntMap.begin(), counterKeys, counterKeys + numCounters);
std::sort(profile->cntMap.begin(), profile->cntMap.end());
assert(std::adjacent_find(profile->cntMap.begin(), profile->cntMap.end())==profile->cntMap.end());
assert(profilesByMethod.find(mh) == profilesByMethod.end());
profilesByMethod[mh] = profile;
newProfiles.push_back(profile);
port_mutex_unlock(&profilesLock);
return profile;
}
bool EdgeProfileCollector::isMethodHot( EdgeMethodProfile* profile )
{
U_32 entryCounter = profile->entryCounter;
if( entryCounter >= eThreshold ){
return true;
}
const U_32 cutoff = bThreshold;
for( U_32 i = 0; i < profile->counters.size(); i++ ){
if( profile->counters[i] >= cutoff ){
return true;
}
}
return false;
}
static void logReadyProfile(const std::string& catName, const std::string& profilerName, EdgeMethodProfile* mp) {
const char* methodName = method_get_name(mp->mh);
Class_Handle ch = method_get_class(mp->mh);
const char* className = class_get_name(ch);
const char* signature = method_get_descriptor(mp->mh);
U_32 backEgdeMaxValue = mp->counters.empty() ? 0 : *std::max_element( mp->counters.begin(), mp->counters.end());
std::ostringstream msg;
msg <<"EM: profiler["<<profilerName.c_str()<<"] profile is ready [e:"
<< mp->entryCounter<<" b:"<<backEgdeMaxValue<<"] " <<className<<"::"<<methodName<<signature;
INFO2(catName.c_str(), msg.str().c_str());
}
void EdgeProfileCollector::onTimeout() {
if(!newProfiles.empty()) {
port_mutex_lock(&profilesLock);
greenProfiles.insert(greenProfiles.end(), newProfiles.begin(), newProfiles.end());
newProfiles.clear();
port_mutex_unlock(&profilesLock);
}
if (!unloadedMethodProfiles.empty()) {
cleanUnloadedProfiles();
}
for (EdgeProfiles::iterator it = greenProfiles.begin(), end = greenProfiles.end(); it!=end; ++it) {
EdgeMethodProfile* profile = *it;
if( isMethodHot( profile ) ){
profile->setHotMethod();
tmpProfiles.push_back(profile);
*it = NULL;
}
}
if (!tmpProfiles.empty()) {
port_mutex_lock(&profilesLock);
std::remove(greenProfiles.begin(), greenProfiles.end(), (EdgeMethodProfile*)NULL);
greenProfiles.resize(greenProfiles.size() - tmpProfiles.size());
port_mutex_unlock(&profilesLock);
for (EdgeProfiles::iterator it = tmpProfiles.begin(), end = tmpProfiles.end(); it!=end; ++it) {
EdgeMethodProfile* profile = *it;
if (loggingEnabled) {
logReadyProfile(catName, name, profile);
}
em->methodProfileIsReady(profile);
}
tmpProfiles.clear();
}
}
void EdgeProfileCollector::cleanUnloadedProfiles() {
for (EdgeProfiles::const_iterator it = unloadedMethodProfiles.begin(), end = unloadedMethodProfiles.end(); it!=end; ++it) {
EdgeMethodProfile* profile = *it;
profilesByMethod.erase(profile->mh);
EdgeProfiles::iterator it2 = std::find(greenProfiles.begin(), greenProfiles.end(), profile);
assert(it2!=greenProfiles.end());
*it2=NULL;
delete profile;
}
unloadedMethodProfiles.clear();
greenProfiles.erase(std::remove(greenProfiles.begin(), greenProfiles.end(), (EdgeMethodProfile*)NULL), greenProfiles.end());
}
static void addProfilesForClassloader(Class_Loader_Handle h, EdgeProfiles& from, EdgeProfiles& to) {
for (EdgeProfiles::iterator it = from.begin(), end = from.end(); it!=end; ++it) {
EdgeMethodProfile* profile = *it;
Class_Handle ch = method_get_class(profile->mh);;
Class_Loader_Handle clh = class_get_class_loader(ch);
if (clh == h) {
to.push_back(profile);
}
}
}
void EdgeProfileCollector::classloaderUnloadingCallback(Class_Loader_Handle h) {
port_mutex_lock(&profilesLock);
//can't modify profiles map in async mode here -> it could be iterated by the checker thread without lock
addProfilesForClassloader(h, greenProfiles, unloadedMethodProfiles);
addProfilesForClassloader(h, newProfiles, unloadedMethodProfiles);
port_mutex_unlock(&profilesLock);
}