• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2023 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "ecmascript/pgo_profiler/pgo_profiler.h"
17 #include <chrono>
18 
19 #include "ecmascript/js_function.h"
20 #include "ecmascript/pgo_profiler/pgo_profiler_manager.h"
21 
22 namespace panda::ecmascript {
ProfileCall(JSTaggedType func,JSTaggedType callTarget,int32_t pcOffset,SampleMode mode,int32_t incCount)23 void PGOProfiler::ProfileCall(JSTaggedType func, JSTaggedType callTarget, int32_t pcOffset, SampleMode mode,
24                               int32_t incCount)
25 {
26     if (!isEnable_) {
27         return;
28     }
29     DISALLOW_GARBAGE_COLLECTION;
30     JSTaggedValue calleeFunc(callTarget);
31     if (!calleeFunc.IsJSFunction()) {
32         return;
33     }
34     if (!JSFunction::Cast(calleeFunc)->GetMethod().IsMethod()) {
35         return;
36     }
37     auto calleeMethod = Method::Cast(JSFunction::Cast(calleeFunc)->GetMethod());
38     JSTaggedValue calleeRecordNameValue = JSFunction::Cast(calleeFunc)->GetRecordName();
39     if (calleeRecordNameValue.IsHole()) {
40         return;
41     }
42     CString calleeRecordName = ConvertToString(calleeRecordNameValue);
43     if (recordInfos_->AddMethod(calleeRecordName, calleeMethod, mode, incCount)) {
44         methodCount_++;
45     }
46     JSTaggedValue currentFunc(func);
47     if (pcOffset > 0 && currentFunc.IsJSFunction() && JSFunction::Cast(currentFunc)->GetMethod().IsMethod()) {
48         auto currentMethod = Method::Cast(JSFunction::Cast(currentFunc)->GetMethod());
49         JSTaggedValue currentRecordNameValue = JSFunction::Cast(currentFunc)->GetRecordName();
50         if (currentRecordNameValue.IsHole()) {
51             return;
52         }
53         if (calleeMethod->IsNativeWithCallField()) {
54             return;
55         }
56         CString currentRecordName = ConvertToString(currentRecordNameValue);
57         // Only mark the call in the same record now
58         if (currentRecordName != calleeRecordName) {
59             return;
60         }
61         auto currentMethodId = currentMethod->GetMethodId();
62         PGOSampleType calleeMethodOffset = PGOSampleType::CreateClassType(calleeMethod->GetMethodId().GetOffset());
63         recordInfos_->AddCallTargetType(currentRecordName, currentMethodId, pcOffset, calleeMethodOffset);
64     }
65     auto interval = std::chrono::system_clock::now() - saveTimestamp_;
66     // Merged every 10 methods and merge interval greater than minimal interval
67     if (methodCount_ >= MERGED_EVERY_COUNT && interval > MERGED_MIN_INTERVAL) {
68         LOG_ECMA(DEBUG) << "Sample: post task to save profiler";
69         PGOProfilerManager::GetInstance()->Merge(this);
70         PGOProfilerManager::GetInstance()->AsynSave();
71         SetSaveTimestamp(std::chrono::system_clock::now());
72         methodCount_ = 0;
73     }
74 }
75 
ProfileOpType(JSTaggedType func,int32_t offset,uint32_t type)76 void PGOProfiler::ProfileOpType(JSTaggedType func, int32_t offset, uint32_t type)
77 {
78     if (!isEnable_) {
79         return;
80     }
81 
82     DISALLOW_GARBAGE_COLLECTION;
83     JSTaggedValue funcValue(func);
84     if (funcValue.IsJSFunction() && JSFunction::Cast(funcValue)->GetMethod().IsMethod()) {
85         auto jsMethod = Method::Cast(JSFunction::Cast(funcValue)->GetMethod());
86         JSTaggedValue recordNameValue = JSFunction::Cast(funcValue)->GetRecordName();
87         if (recordNameValue.IsHole()) {
88             return;
89         }
90         CString recordName = ConvertToString(recordNameValue);
91         recordInfos_->AddType(recordName, jsMethod->GetMethodId(), offset, PGOSampleType(type));
92     }
93 }
94 
ProfileDefineClass(JSThread * thread,JSTaggedType func,int32_t offset,JSTaggedType ctor)95 void PGOProfiler::ProfileDefineClass(JSThread *thread, JSTaggedType func, int32_t offset, JSTaggedType ctor)
96 {
97     if (!isEnable_) {
98         return;
99     }
100 
101     DISALLOW_GARBAGE_COLLECTION;
102     JSTaggedValue funcValue(func);
103     if (funcValue.IsJSFunction()) {
104         JSFunction *funcFunction = JSFunction::Cast(funcValue);
105         JSTaggedValue recordNameValue = funcFunction->GetRecordName();
106         if (recordNameValue.IsHole()) {
107             return;
108         }
109         CString recordName = ConvertToString(recordNameValue);
110 
111         auto method = funcFunction->GetMethod();
112         if (!method.IsMethod()) {
113             return;
114         }
115         auto jsMethod = Method::Cast(method);
116         auto funcMethodId = jsMethod->GetMethodId();
117 
118         JSHandle<JSTaggedValue> ctorValue(thread, JSTaggedValue(ctor));
119         if (!ctorValue->IsJSFunction()) {
120             return;
121         }
122         JSFunction *ctorFunction = JSFunction::Cast(ctorValue->GetTaggedObject());
123         auto ctorMethod = ctorFunction->GetMethod();
124         if (!ctorMethod.IsMethod()) {
125             return;
126         }
127         auto ctorJSMethod = Method::Cast(ctorMethod);
128         int32_t ctorMethodId = static_cast<int32_t>(ctorJSMethod->GetMethodId().GetOffset());
129         auto currentType = PGOSampleType::CreateClassType(ctorMethodId);
130 
131         auto superFuncValue = JSTaggedValue::GetPrototype(thread, ctorValue);
132         RETURN_IF_ABRUPT_COMPLETION(thread);
133         PGOSampleType superType = PGOSampleType::CreateClassType(0);
134         if (superFuncValue.IsJSFunction()) {
135             auto superFuncFunction = JSFunction::Cast(superFuncValue);
136             if (superFuncFunction->GetMethod().IsMethod()) {
137                 auto superMethod = Method::Cast(superFuncFunction->GetMethod());
138                 auto superMethodId = superMethod->GetMethodId().GetOffset();
139                 superType = PGOSampleType::CreateClassType(superMethodId);
140             }
141         }
142         recordInfos_->AddDefine(recordName, funcMethodId, offset, currentType, superType);
143 
144         auto prototype = ctorFunction->GetProtoOrHClass();
145         if (!prototype.IsJSObject()) {
146             return;
147         }
148         auto prototypeObj = JSObject::Cast(prototype);
149         auto prototypeHClass = JSTaggedType(prototypeObj->GetClass());
150         recordInfos_->AddLayout(currentType, prototypeHClass, PGOObjKind::PROTOTYPE);
151 
152         auto ctorHClass = JSTaggedType(ctorFunction->GetJSHClass());
153         recordInfos_->AddLayout(currentType, ctorHClass, PGOObjKind::CONSTRUCTOR);
154     }
155 }
156 
ProfileCreateObject(JSTaggedType func,int32_t offset,JSTaggedType newObj,int32_t traceId)157 void PGOProfiler::ProfileCreateObject(JSTaggedType func, int32_t offset, JSTaggedType newObj, int32_t traceId)
158 {
159     if (!isEnable_) {
160         return;
161     }
162 
163     DISALLOW_GARBAGE_COLLECTION;
164     JSTaggedValue funcValue(func);
165     if (funcValue.IsJSFunction()) {
166         JSFunction *funcFunction = JSFunction::Cast(funcValue);
167         JSTaggedValue recordNameValue = funcFunction->GetRecordName();
168         if (recordNameValue.IsHole()) {
169             return;
170         }
171         CString recordName = ConvertToString(recordNameValue);
172 
173         auto method = funcFunction->GetMethod();
174         if (!method.IsMethod()) {
175             return;
176         }
177         auto jsMethod = Method::Cast(method);
178         auto funcMethodId = jsMethod->GetMethodId();
179 
180         auto newObjValue = JSTaggedValue(newObj);
181         if (!newObjValue.IsJSObject()) {
182             return;
183         }
184         auto newHClass = JSObject::Cast(newObjValue) ->GetJSHClass();
185         if (newHClass->IsJSArray()) {
186             auto array = JSArray::Cast(newObjValue);
187             auto currentType = PGOSampleType::CreateClassType(array->GetTraceIndex());
188             auto superType = PGOSampleType::CreateClassType(0);
189             recordInfos_->AddDefine(recordName, funcMethodId, offset, currentType, superType);
190             PGOObjKind kind = PGOObjKind::ELEMENT;
191             recordInfos_->AddLayout(currentType, JSTaggedType(newHClass), kind);
192         } else {
193             InsertLiteralId(JSTaggedType(newHClass), traceId);
194             auto currentType = PGOSampleType::CreateClassType(traceId);
195             auto superType = PGOSampleType::CreateClassType(0);
196             recordInfos_->AddDefine(recordName, funcMethodId, offset, currentType, superType);
197             PGOObjKind kind = PGOObjKind::LOCAL;
198             recordInfos_->AddLayout(currentType, JSTaggedType(newHClass), kind);
199         }
200     }
201 }
202 
ProfileObjLayout(JSThread * thread,JSTaggedType func,int32_t offset,JSTaggedType object,bool store)203 void PGOProfiler::ProfileObjLayout(JSThread *thread, JSTaggedType func, int32_t offset, JSTaggedType object, bool store)
204 {
205     if (!isEnable_) {
206         return;
207     }
208 
209     DISALLOW_GARBAGE_COLLECTION;
210     JSTaggedValue funcValue(func);
211     if (funcValue.IsJSFunction()) {
212         JSFunction *funcFunction = JSFunction::Cast(funcValue);
213         auto method = funcFunction->GetMethod();
214         if (!method.IsMethod()) {
215             return;
216         }
217         auto jsMethod = Method::Cast(method);
218         JSTaggedValue recordNameValue = funcFunction->GetRecordName();
219         if (recordNameValue.IsHole()) {
220             return;
221         }
222         CString recordName = ConvertToString(recordNameValue);
223 
224         auto holder = JSTaggedValue(object);
225         auto hclass = holder.GetTaggedObject()->GetClass();
226         auto ctor = JSTaggedValue::Undefined();
227         PGOObjKind kind = PGOObjKind::LOCAL;
228         if (hclass->IsClassPrototype()) {
229             ctor = JSObject::GetCtorFromPrototype(thread, holder);
230             kind = PGOObjKind::PROTOTYPE;
231         } else if (hclass->IsClassConstructor()) {
232             ctor = holder;
233             kind = PGOObjKind::CONSTRUCTOR;
234         } else if (hclass->IsLiteral()) {
235             auto iter = traceIds_.find(JSTaggedType(hclass));
236             if (iter != traceIds_.end()) {
237                 PGOObjectInfo info(ClassType(iter->second), kind);
238                 auto methodId = jsMethod->GetMethodId();
239                 recordInfos_->AddObjectInfo(recordName, methodId, offset, info);
240                 if (store) {
241                     auto type = PGOSampleType::CreateClassType(iter->second);
242                     recordInfos_->AddLayout(type, JSTaggedType(hclass), kind);
243                 }
244             }
245             return;
246         } else if (hclass->IsJSArray()) {
247             kind = PGOObjKind::ELEMENT;
248             auto array = JSArray::Cast(holder);
249             if (array->GetTraceIndex() != 0) {
250                 PGOObjectInfo info(ClassType(array->GetTraceIndex()), kind);
251                 auto methodId = jsMethod->GetMethodId();
252                 recordInfos_->AddObjectInfo(recordName, methodId, offset, info);
253                 if (store) {
254                     auto type = PGOSampleType::CreateClassType(array->GetTraceIndex());
255                     recordInfos_->AddLayout(type, JSTaggedType(hclass), kind);
256                 }
257             }
258             return;
259         } else {
260             auto prototype = hclass->GetProto();
261             ctor = JSObject::GetCtorFromPrototype(thread, prototype);
262         }
263 
264         if (ctor.IsJSFunction()) {
265             auto ctorFunc = JSFunction::Cast(ctor);
266             auto ctorMethod = ctorFunc->GetMethod();
267             if (!ctorMethod.IsMethod()) {
268                 return;
269             }
270             auto ctorJSMethod = Method::Cast(ctorMethod);
271             auto methodId = ctorJSMethod->GetMethodId();
272             PGOObjectInfo info(ClassType(methodId.GetOffset()), kind);
273             recordInfos_->AddObjectInfo(recordName, jsMethod->GetMethodId(), offset, info);
274             if (store) {
275                 PGOSampleType type = PGOSampleType::CreateClassType(methodId.GetOffset());
276                 recordInfos_->AddLayout(type, JSTaggedType(hclass), kind);
277             }
278         }
279     }
280 }
281 
InsertLiteralId(JSTaggedType hclass,int32_t traceId)282 void PGOProfiler::InsertLiteralId(JSTaggedType hclass, int32_t traceId)
283 {
284     if (!isEnable_) {
285         return;
286     }
287     auto iter = traceIds_.find(hclass);
288     if (iter != traceIds_.end() && iter->second != traceId) {
289         traceIds_.erase(iter);
290     }
291     traceIds_.emplace(hclass, traceId);
292 }
293 
ProcessReferences(const WeakRootVisitor & visitor)294 void PGOProfiler::ProcessReferences(const WeakRootVisitor &visitor)
295 {
296     if (!isEnable_) {
297         return;
298     }
299     for (auto iter = traceIds_.begin(); iter != traceIds_.end();) {
300         JSTaggedType object = iter->first;
301         auto fwd = visitor(reinterpret_cast<TaggedObject *>(object));
302         if (fwd == nullptr) {
303             iter = traceIds_.erase(iter);
304             continue;
305         }
306         if (fwd != reinterpret_cast<TaggedObject *>(object)) {
307             UNREACHABLE();
308         }
309         ++iter;
310     }
311 }
312 } // namespace panda::ecmascript
313