Skip to content

Commit 08c8e7e

Browse files
author
Mihail Slavchev
committed
Workaround for V8 profiler in order to show correct function names in CPU
profiles. By default, V8 maps all native functions to the address of the C++ handler (MetadataNode::MethodCallback). As a result there is only one entry (the last one) in the map for all Java methods. While using libffi to generate unique native handler with sole purpose to forward the call to MetadataNode::MethodCallback works fine on Windows, it does not work for Android x86/arm because V8 profiler cannot unwind properly the stack. Hence, we wrap all Java methods with JS functions and take advantage of ScriptOrigin.
1 parent e7db2e2 commit 08c8e7e

4 files changed

Lines changed: 217 additions & 76 deletions

File tree

src/jni/MetadataNode.cpp

Lines changed: 201 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ void MetadataNode::Init(Isolate *isolate)
2525
}
2626

2727
MetadataNode::MetadataNode(MetadataTreeNode *treeNode) :
28-
m_treeNode(treeNode)
28+
m_treeNode(treeNode), m_poCtorFunc(nullptr)
2929
{
3030
uint8_t nodeType = s_metadataReader.GetNodeType(treeNode);
3131

@@ -453,17 +453,21 @@ void MetadataNode::SuperAccessorGetterCallback(Local<String> property, const Pro
453453
}
454454
}
455455

456-
Local<Function> MetadataNode::SetMembers(Isolate *isolate, Local<FunctionTemplate>& ctorFuncTemplate, Local<ObjectTemplate>& prototypeTemplate, vector<MethodCallbackData*>& instanceMethodsCallbackData, const vector<MethodCallbackData*>& baseInstanceMethodsCallbackData, MetadataTreeNode *treeNode)
456+
void MetadataNode::SetInstanceMembers(Isolate *isolate, Local<FunctionTemplate>& ctorFuncTemplate, Local<ObjectTemplate>& prototypeTemplate, vector<MethodCallbackData*>& instanceMethodsCallbackData, const vector<MethodCallbackData*>& baseInstanceMethodsCallbackData, MetadataTreeNode *treeNode)
457457
{
458458
auto hasCustomMetadata = treeNode->metadata != nullptr;
459459

460-
return hasCustomMetadata
461-
? SetMembersFromRuntimeMetadata(isolate, ctorFuncTemplate, prototypeTemplate, instanceMethodsCallbackData, baseInstanceMethodsCallbackData, treeNode)
462-
:
463-
SetMembersFromStaticMetadata(isolate, ctorFuncTemplate, prototypeTemplate, instanceMethodsCallbackData, baseInstanceMethodsCallbackData, treeNode);
460+
if (hasCustomMetadata)
461+
{
462+
SetInstanceMembersFromRuntimeMetadata(isolate, ctorFuncTemplate, prototypeTemplate, instanceMethodsCallbackData, baseInstanceMethodsCallbackData, treeNode);
463+
}
464+
else
465+
{
466+
SetInstanceMembersFromStaticMetadata(isolate, ctorFuncTemplate, prototypeTemplate, instanceMethodsCallbackData, baseInstanceMethodsCallbackData, treeNode);
467+
}
464468
}
465469

466-
Local<Function> MetadataNode::SetMembersFromStaticMetadata(Isolate *isolate, Local<FunctionTemplate>& ctorFuncTemplate, Local<ObjectTemplate>& prototypeTemplate, vector<MethodCallbackData*>& instanceMethodsCallbackData, const vector<MethodCallbackData*>& baseInstanceMethodsCallbackData, MetadataTreeNode *treeNode)
470+
void MetadataNode::SetInstanceMembersFromStaticMetadata(Isolate *isolate, Local<FunctionTemplate>& ctorFuncTemplate, Local<ObjectTemplate>& prototypeTemplate, vector<MethodCallbackData*>& instanceMethodsCallbackData, const vector<MethodCallbackData*>& baseInstanceMethodsCallbackData, MetadataTreeNode *treeNode)
467471
{
468472
SET_PROFILER_FRAME();
469473

@@ -511,8 +515,7 @@ Local<Function> MetadataNode::SetMembersFromStaticMetadata(Isolate *isolate, Loc
511515
auto funcTemplate = FunctionTemplate::New(isolate, MethodCallback, funcData);
512516
auto func = funcTemplate->GetFunction();
513517
auto funcName = ConvertToV8String(entry.name);
514-
func->SetName(funcName);
515-
prototypeTemplate->Set(funcName, func);
518+
prototypeTemplate->Set(funcName, Wrap(isolate, func, entry.name, false /* isCtorFunc */));
516519
lastMethodName = entry.name;
517520
}
518521

@@ -532,59 +535,9 @@ Local<Function> MetadataNode::SetMembersFromStaticMetadata(Isolate *isolate, Loc
532535
auto fieldData = External::New(isolate, fieldInfo);
533536
prototypeTemplate->SetAccessor(fieldName, FieldAccessorGetterCallback, FieldAccessorSetterCallback, fieldData, AccessControl::DEFAULT, PropertyAttribute::DontDelete);
534537
}
535-
536-
ctorFunction = ctorFuncTemplate->GetFunction();
537-
538-
//get candidates from static methods metadata
539-
callbackData = nullptr;
540-
lastMethodName.clear();
541-
auto staticMethodCout = *reinterpret_cast<uint16_t*>(curPtr);
542-
curPtr += sizeof(uint16_t);
543-
for (auto i = 0; i < staticMethodCout; i++)
544-
{
545-
auto entry = s_metadataReader.ReadStaticMethodEntry(&curPtr);
546-
if (entry.name != lastMethodName)
547-
{
548-
callbackData = new MethodCallbackData(this);
549-
auto funcData = External::New(isolate, callbackData);
550-
auto funcTemplate = FunctionTemplate::New(isolate, MethodCallback, funcData);
551-
auto func = funcTemplate->GetFunction();
552-
auto funcName = ConvertToV8String(entry.name);
553-
func->SetName(funcName);
554-
ctorFunction->Set(funcName, func);
555-
lastMethodName = entry.name;
556-
}
557-
callbackData->candidates.push_back(entry);
558-
}
559-
560-
//attach .extend function
561-
auto extendFuncName = V8StringConstants::GetExtend();
562-
auto extendFuncTemplate = FunctionTemplate::New(isolate, ExtendCallMethodCallback, External::New(isolate, this));
563-
ctorFunction->Set(extendFuncName, extendFuncTemplate->GetFunction());
564-
565-
//get candidates from static fields metadata
566-
auto staticFieldCout = *reinterpret_cast<uint16_t*>(curPtr);
567-
curPtr += sizeof(uint16_t);
568-
for (auto i = 0; i < staticFieldCout; i++)
569-
{
570-
auto entry = s_metadataReader.ReadStaticFieldEntry(&curPtr);
571-
572-
auto fieldName = ConvertToV8String(entry.name);
573-
auto fieldData = External::New(isolate, new FieldCallbackData(entry));
574-
ctorFunction->SetAccessor(fieldName, FieldAccessorGetterCallback, FieldAccessorSetterCallback, fieldData, AccessControl::DEFAULT, PropertyAttribute::DontDelete);
575-
}
576-
577-
auto nullObjectName = V8StringConstants::GetNullObject();
578-
579-
Local<Value> nullObjectData = External::New(isolate, this);
580-
ctorFunction->SetAccessor(nullObjectName, NullObjectAccessorGetterCallback, nullptr, nullObjectData);
581-
582-
SetClassAccessor(ctorFunction);
583-
584-
return ctorFunction;
585538
}
586539

587-
Local<Function> MetadataNode::SetMembersFromRuntimeMetadata(Isolate *isolate, Local<FunctionTemplate>& ctorFuncTemplate, Local<ObjectTemplate>& prototypeTemplate, vector<MethodCallbackData*>& instanceMethodsCallbackData, const vector<MethodCallbackData*>& baseInstanceMethodsCallbackData, MetadataTreeNode *treeNode)
540+
void MetadataNode::SetInstanceMembersFromRuntimeMetadata(Isolate *isolate, Local<FunctionTemplate>& ctorFuncTemplate, Local<ObjectTemplate>& prototypeTemplate, vector<MethodCallbackData*>& instanceMethodsCallbackData, const vector<MethodCallbackData*>& baseInstanceMethodsCallbackData, MetadataTreeNode *treeNode)
588541
{
589542
SET_PROFILER_FRAME();
590543

@@ -642,7 +595,6 @@ Local<Function> MetadataNode::SetMembersFromRuntimeMetadata(Isolate *isolate, Lo
642595
auto funcTemplate = FunctionTemplate::New(isolate, MethodCallback, funcData);
643596
auto func = funcTemplate->GetFunction();
644597
auto funcName = ConvertToV8String(entry.name);
645-
func->SetName(funcName);
646598
prototypeTemplate->Set(funcName, func);
647599
lastMethodName = entry.name;
648600
}
@@ -656,12 +608,85 @@ Local<Function> MetadataNode::SetMembersFromRuntimeMetadata(Isolate *isolate, Lo
656608
prototypeTemplate->SetAccessor(fieldName, FieldAccessorGetterCallback, FieldAccessorSetterCallback, fieldData, access, PropertyAttribute::DontDelete);
657609
}
658610
}
611+
}
612+
613+
void MetadataNode::SetStaticMembers(Isolate *isolate, Local<Function>& ctorFunction, MetadataTreeNode *treeNode)
614+
{
615+
auto hasCustomMetadata = treeNode->metadata != nullptr;
616+
617+
if (!hasCustomMetadata)
618+
{
619+
uint8_t *curPtr = s_metadataReader.GetValueData() + treeNode->offsetValue + 1;
620+
auto nodeType = s_metadataReader.GetNodeType(treeNode);
621+
auto curType = s_metadataReader.ReadTypeName(treeNode);
622+
curPtr += sizeof(uint16_t /* baseClassId */);
623+
if (s_metadataReader.IsNodeTypeInterface(nodeType))
624+
{
625+
curPtr += sizeof(uint8_t) + sizeof(uint32_t);
626+
}
627+
auto instanceMethodCout = *reinterpret_cast<uint16_t*>(curPtr);
628+
curPtr += sizeof(uint16_t);
629+
for (auto i = 0; i < instanceMethodCout; i++)
630+
{
631+
auto entry = s_metadataReader.ReadInstanceMethodEntry(&curPtr);
632+
}
633+
auto instanceFieldCout = *reinterpret_cast<uint16_t*>(curPtr);
634+
curPtr += sizeof(uint16_t);
635+
for (auto i = 0; i < instanceFieldCout; i++)
636+
{
637+
auto entry = s_metadataReader.ReadInstanceFieldEntry(&curPtr);
638+
}
639+
640+
string lastMethodName;
641+
MethodCallbackData *callbackData = nullptr;
642+
643+
//get candidates from static methods metadata
644+
auto staticMethodCout = *reinterpret_cast<uint16_t*>(curPtr);
645+
curPtr += sizeof(uint16_t);
646+
for (auto i = 0; i < staticMethodCout; i++)
647+
{
648+
auto entry = s_metadataReader.ReadStaticMethodEntry(&curPtr);
649+
if (entry.name != lastMethodName)
650+
{
651+
callbackData = new MethodCallbackData(this);
652+
auto funcData = External::New(isolate, callbackData);
653+
auto funcTemplate = FunctionTemplate::New(isolate, MethodCallback, funcData);
654+
auto func = funcTemplate->GetFunction();
655+
auto funcName = ConvertToV8String(entry.name);
656+
ctorFunction->Set(funcName, Wrap(isolate, func, entry.name, false /* isCtorFunc */));
657+
lastMethodName = entry.name;
658+
}
659+
callbackData->candidates.push_back(entry);
660+
}
661+
662+
//attach .extend function
663+
auto extendFuncName = V8StringConstants::GetExtend();
664+
auto extendFuncTemplate = FunctionTemplate::New(isolate, ExtendCallMethodCallback, External::New(isolate, this));
665+
ctorFunction->Set(extendFuncName, extendFuncTemplate->GetFunction());
666+
667+
//get candidates from static fields metadata
668+
auto staticFieldCout = *reinterpret_cast<uint16_t*>(curPtr);
669+
curPtr += sizeof(uint16_t);
670+
for (auto i = 0; i < staticFieldCout; i++)
671+
{
672+
auto entry = s_metadataReader.ReadStaticFieldEntry(&curPtr);
659673

660-
auto ctorFunction = ctorFuncTemplate->GetFunction();
674+
auto fieldName = ConvertToV8String(entry.name);
675+
auto fieldData = External::New(isolate, new FieldCallbackData(entry));
676+
ctorFunction->SetAccessor(fieldName, FieldAccessorGetterCallback, FieldAccessorSetterCallback, fieldData, AccessControl::DEFAULT, PropertyAttribute::DontDelete);
677+
}
678+
679+
auto nullObjectName = V8StringConstants::GetNullObject();
661680

662-
return ctorFunction;
681+
Local<Value> nullObjectData = External::New(isolate, this);
682+
ctorFunction->SetAccessor(nullObjectName, NullObjectAccessorGetterCallback, nullptr, nullObjectData);
683+
684+
SetClassAccessor(ctorFunction);
685+
}
663686
}
664687

688+
689+
665690
void MetadataNode::InnerClassConstructorCallback(const v8::FunctionCallbackInfo<v8::Value>& info)
666691
{
667692
try
@@ -760,7 +785,7 @@ void MetadataNode::SetInnnerTypes(Isolate *isolate, Local<Function>& ctorFunctio
760785
if (isStatic)
761786
{
762787
auto innerTypeCtorFuncTemplate = childNode->GetConstructorFunctionTemplate(isolate, curChild);
763-
auto innerTypeCtorFunc = innerTypeCtorFuncTemplate->GetFunction();
788+
auto innerTypeCtorFunc = Local<Function>::New(isolate, *GetOrCreateInternal(curChild)->m_poCtorFunc);
764789
auto innerTypeName = ConvertToV8String(curChild->name);
765790
ctorFunction->Set(innerTypeName, innerTypeCtorFunc);
766791
}
@@ -804,42 +829,50 @@ Local<FunctionTemplate> MetadataNode::GetConstructorFunctionTemplate(Isolate *is
804829
ctorFuncTemplate = FunctionTemplate::New(isolate, funcCallback, ctorCallbackData);
805830
ctorFuncTemplate->InstanceTemplate()->SetInternalFieldCount(static_cast<int>(ObjectManager::MetadataNodeKeys::END));
806831

807-
auto baseClass = s_metadataReader.GetBaseClassNode(treeNode);
832+
auto baseTreeNode = s_metadataReader.GetBaseClassNode(treeNode);
808833
Local<Function> baseCtorFunc;
809834
vector<MethodCallbackData*> baseInstanceMethodsCallbackData;
810-
if ((baseClass != treeNode) && (baseClass != nullptr) && (baseClass->offsetValue > 0))
835+
if ((baseTreeNode != treeNode) && (baseTreeNode != nullptr) && (baseTreeNode->offsetValue > 0))
811836
{
812-
auto baseFuncTemplate = GetConstructorFunctionTemplate(isolate, baseClass, baseInstanceMethodsCallbackData);
837+
auto baseFuncTemplate = GetConstructorFunctionTemplate(isolate, baseTreeNode, baseInstanceMethodsCallbackData);
813838
if (!baseFuncTemplate.IsEmpty())
814839
{
815840
ctorFuncTemplate->Inherit(baseFuncTemplate);
816-
baseCtorFunc = baseFuncTemplate->GetFunction();
841+
baseCtorFunc = Local<Function>::New(isolate, *GetOrCreateInternal(baseTreeNode)->m_poCtorFunc);
817842
}
818843
}
819844

820845
auto prototypeTemplate = ctorFuncTemplate->PrototypeTemplate();
821846

822-
auto ctorFunc = node->SetMembers(isolate, ctorFuncTemplate, prototypeTemplate, instanceMethodsCallbackData, baseInstanceMethodsCallbackData, treeNode);
847+
node->SetInstanceMembers(isolate, ctorFuncTemplate, prototypeTemplate, instanceMethodsCallbackData, baseInstanceMethodsCallbackData, treeNode);
848+
849+
auto ctorFunc = ctorFuncTemplate->GetFunction();
850+
851+
auto wrappedCtorFunc = Wrap(isolate, ctorFunc, node->m_treeNode->name, true /* isCtorFunc */);
852+
853+
node->SetStaticMembers(isolate, wrappedCtorFunc, treeNode);
854+
855+
node->m_poCtorFunc = new Persistent<Function>(isolate, wrappedCtorFunc);
823856
if (!baseCtorFunc.IsEmpty())
824857
{
825-
ctorFunc->SetPrototype(baseCtorFunc);
858+
wrappedCtorFunc->SetPrototype(baseCtorFunc);
826859
}
827860

828861
auto pft = new Persistent<FunctionTemplate>(isolate, ctorFuncTemplate);
829862
CtorCacheItem ctorCacheItem(pft, instanceMethodsCallbackData);
830863
cache->CtorFuncCache.insert(make_pair(treeNode, ctorCacheItem));
831864

832-
SetInnnerTypes(isolate, ctorFunc, treeNode);
865+
SetInnnerTypes(isolate, wrappedCtorFunc, treeNode);
833866

834-
SetTypeMetadata(isolate, ctorFunc, new TypeMetadata(s_metadataReader.ReadTypeName(treeNode)));
867+
SetTypeMetadata(isolate, wrappedCtorFunc, new TypeMetadata(s_metadataReader.ReadTypeName(treeNode)));
835868

836869
return ctorFuncTemplate;
837870
}
838871

839872
Local<Function> MetadataNode::GetConstructorFunction(Isolate *isolate)
840873
{
841874
auto ctorFuncTemplate = GetConstructorFunctionTemplate(isolate, m_treeNode);
842-
auto ctorFunc = ctorFuncTemplate->GetFunction();
875+
auto ctorFunc = Local<Function>::New(isolate, *m_poCtorFunc);
843876
return ctorFunc;
844877
}
845878

@@ -1775,9 +1808,104 @@ MetadataNode::MetadataNodeCache* MetadataNode::GetCache(Isolate *isolate)
17751808
return cache;
17761809
}
17771810

1811+
void MetadataNode::EnableProfiler(bool enableProfiler)
1812+
{
1813+
s_profilerEnabled = enableProfiler;
1814+
}
1815+
1816+
Local<Function> MetadataNode::Wrap(Isolate* isolate, const Local<Function>& f, const string& name, bool isCtorFunc)
1817+
{
1818+
if (!s_profilerEnabled)
1819+
{
1820+
return f;
1821+
}
1822+
1823+
static set<string> keywords;
1824+
1825+
if (keywords.empty())
1826+
{
1827+
string kw[] { "abstract", "arguments", "boolean", "break", "byte", "case", "catch", "char", "class", "const", "continue", "debugger", "default", "delete", "do",
1828+
"double", "else", "enum", "eval", "export", "extends", "false", "final", "finally", "float", "for", "function", "goto", "if", "implements",
1829+
"import", "in", "instanceof", "int", "interface", "let", "long", "native", "new", "null", "package", "private", "protected", "public", "return",
1830+
"short", "static", "super", "switch", "synchronized", "this", "throw", "throws", "transient", "true", "try", "typeof", "var", "void", "volatile", "while", "with", "yield" };
1831+
1832+
keywords = set<string>(kw, kw + sizeof(kw)/sizeof(kw[0]));
1833+
}
1834+
1835+
if (name == "<init>")
1836+
{
1837+
return f;
1838+
}
1839+
1840+
string actualName = name;
1841+
while (keywords.find(actualName) != keywords.end())
1842+
{
1843+
actualName.append("_");
1844+
}
1845+
1846+
Local<Function> ret;
1847+
1848+
stringstream ss;
1849+
ss << "(function() { ";
1850+
ss << "function " << actualName << "() { ";
1851+
if (isCtorFunc)
1852+
{
1853+
ss << "var args = [null]; for (var i=0; i<arguments.length; i++) { args.push(arguments[i]); }; ";
1854+
ss << "return new (Function.prototype.bind.apply(" << actualName << ".__func, args)); ";
1855+
}
1856+
else
1857+
{
1858+
ss << "return " << actualName << ".__func.apply(this, arguments); ";
1859+
}
1860+
ss << "} ";
1861+
ss << "return " << actualName << "; ";
1862+
ss << "})()";
1863+
1864+
auto str = ss.str();
1865+
auto source = ConvertToV8String(str);
1866+
auto context = isolate->GetCurrentContext();
1867+
1868+
TryCatch tc;
1869+
1870+
Local<Script> script;
1871+
ScriptOrigin origin(ConvertToV8String(Constants::APP_ROOT_FOLDER_PATH + m_name));
1872+
auto maybeScript = Script::Compile(context, source, &origin).ToLocal(&script);
1873+
1874+
if (tc.HasCaught())
1875+
{
1876+
throw NativeScriptException(tc, "Cannot compile wrapper");
1877+
}
1878+
1879+
if (!script.IsEmpty())
1880+
{
1881+
Local<Value> result;
1882+
auto maybeResult = script->Run(context).ToLocal(&result);
1883+
1884+
if (!result.IsEmpty())
1885+
{
1886+
ret = result.As<Function>();
1887+
ret->Set(ConvertToV8String("__func"), f);
1888+
1889+
auto prototypePropName = ConvertToV8String("prototype");
1890+
ret->Set(prototypePropName, f->Get(prototypePropName));
1891+
}
1892+
else
1893+
{
1894+
throw NativeScriptException("Cannot create wrapper function");
1895+
}
1896+
}
1897+
else
1898+
{
1899+
throw NativeScriptException(str);
1900+
}
1901+
1902+
return ret;
1903+
}
1904+
17781905
string MetadataNode::TNS_PREFIX = "com/tns/gen/";
17791906
MetadataReader MetadataNode::s_metadataReader;
17801907
std::map<std::string, MetadataNode*> MetadataNode::s_name2NodeCache;
17811908
std::map<std::string, MetadataTreeNode*> MetadataNode::s_name2TreeNodeCache;
17821909
std::map<MetadataTreeNode*, MetadataNode*> MetadataNode::s_treeNode2NodeCache;
17831910
map<Isolate*, MetadataNode::MetadataNodeCache*> MetadataNode::s_cache;
1911+
bool MetadataNode::s_profilerEnabled = false;

0 commit comments

Comments
 (0)