1 /*
2   This file is provided under a dual BSD/GPLv2 license.  When using or
3   redistributing this file, you may do so under either license.
4 
5   GPL LICENSE SUMMARY
6 
7   Copyright(c) 2005-2012 Intel Corporation. All rights reserved.
8 
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of version 2 of the GNU General Public License as
11   published by the Free Software Foundation.
12 
13   This program is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16   General Public License for more details.
17 
18   You should have received a copy of the GNU General Public License
19   along with this program; if not, write to the Free Software
20   Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
21   The full GNU General Public License is included in this distribution
22   in the file called LICENSE.GPL.
23 
24   Contact Information:
25   http://software.intel.com/en-us/articles/intel-vtune-amplifier-xe/
26 
27   BSD LICENSE
28 
29   Copyright(c) 2005-2012 Intel Corporation. All rights reserved.
30   All rights reserved.
31 
32   Redistribution and use in source and binary forms, with or without
33   modification, are permitted provided that the following conditions
34   are met:
35 
36     * Redistributions of source code must retain the above copyright
37       notice, this list of conditions and the following disclaimer.
38     * Redistributions in binary form must reproduce the above copyright
39       notice, this list of conditions and the following disclaimer in
40       the documentation and/or other materials provided with the
41       distribution.
42     * Neither the name of Intel Corporation nor the names of its
43       contributors may be used to endorse or promote products derived
44       from this software without specific prior written permission.
45 
46   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
47   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
48   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
49   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
50   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
51   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
52   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
53   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
54   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
55   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
56   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
57 */
58 
59 #include "vtune-jit.h"
60 
61 #include <stdlib.h>
62 #include <string.h>
63 
64 #include <list>
65 #include <memory>
66 #include <string>
67 #include <unordered_map>
68 #include <vector>
69 
70 #include "../../../include/v8-callbacks.h"
71 #include "../../../include/v8-initialization.h"
72 #include "../../../include/v8-local-handle.h"
73 #include "../../../include/v8-primitive.h"
74 #include "../../../include/v8-script.h"
75 #include "v8-vtune.h"
76 
77 namespace vTune {
78 namespace internal {
79 
80 
81 // This class is used to record the JITted code position info for JIT
82 // code profiling.
83 class JITCodeLineInfo {
84  public:
JITCodeLineInfo()85   JITCodeLineInfo() { }
86 
SetPosition(intptr_t pc,int pos)87   void SetPosition(intptr_t pc, int pos) {
88     AddCodeLineInfo(LineNumInfo(pc, pos));
89   }
90 
91   struct LineNumInfo {
LineNumInfovTune::internal::JITCodeLineInfo::LineNumInfo92     LineNumInfo(intptr_t pc, int pos)
93         : pc_(pc), pos_(pos) { }
94 
95     intptr_t pc_;
96     int pos_;
97   };
98 
GetLineNumInfo()99   std::list<LineNumInfo>* GetLineNumInfo() {
100     return &line_num_info_;
101   }
102 
103  private:
AddCodeLineInfo(const LineNumInfo & line_info)104   void AddCodeLineInfo(const LineNumInfo& line_info) {
105 	  line_num_info_.push_back(line_info);
106   }
107   std::list<LineNumInfo> line_num_info_;
108 };
109 
110 struct SameCodeObjects {
operator ()vTune::internal::SameCodeObjects111   bool operator () (void* key1, void* key2) const {
112     return key1 == key2;
113   }
114 };
115 
116 struct HashForCodeObject {
operator ()vTune::internal::HashForCodeObject117   uint32_t operator () (void* code) const {
118     static const uintptr_t kGoldenRatio = 2654435761u;
119     uintptr_t hash = reinterpret_cast<uintptr_t>(code);
120     return static_cast<uint32_t>(hash * kGoldenRatio);
121   }
122 };
123 
124 typedef std::unordered_map<void*, void*, HashForCodeObject, SameCodeObjects>
125     JitInfoMap;
126 
GetEntries()127 static JitInfoMap* GetEntries() {
128   static JitInfoMap* entries;
129   if (entries == NULL) {
130     entries = new JitInfoMap();
131   }
132   return entries;
133 }
134 
IsLineInfoTagged(void * ptr)135 static bool IsLineInfoTagged(void* ptr) {
136   return 0 != (reinterpret_cast<intptr_t>(ptr));
137 }
138 
UntagLineInfo(void * ptr)139 static JITCodeLineInfo* UntagLineInfo(void* ptr) {
140   return reinterpret_cast<JITCodeLineInfo*>(
141     reinterpret_cast<intptr_t>(ptr));
142 }
143 
144 // The parameter str is a mixed pattern which contains the
145 // function name and some other info. It comes from all the
146 // Logger::CodeCreateEvent(...) function. This function get the
147 // pure function name from the input parameter.
GetFunctionNameFromMixedName(const char * str,int length)148 static std::string GetFunctionNameFromMixedName(const char* str, int length) {
149   int index = 0;
150   int count = 0;
151   char* start_ptr = NULL;
152 
153   while (str[index++] != ':' && (index < length)) {}
154 
155   const char state = str[index];
156   if (state == '*' || state == '+' || state == '-' || state == '~') index++;
157   if (index >= length) return std::string();
158 
159   start_ptr = const_cast<char*>(str + index);
160 
161   // Detecting JS and WASM function names. In JitCodeEvent->name.str
162   // JS functions name ends with a space symbol. WASM function name
163   // ends with the latest closing parenthesis.
164   char last_char = ' ';
165   int parenthesis_count = 0;
166   while (index < length) {
167     if (str[index] == '(') {
168       last_char = ')';
169       parenthesis_count++;
170     }
171     if (str[index] == ')') parenthesis_count--;
172     if (str[index] == last_char && parenthesis_count == 0) {
173       if (last_char == ')') count++;
174       break;
175     }
176     count++;
177     index++;
178   }
179 
180   return std::string(start_ptr, count);
181 }
182 
183 // The JitCodeEventHandler for Vtune.
event_handler(const v8::JitCodeEvent * event)184 void VTUNEJITInterface::event_handler(const v8::JitCodeEvent* event) {
185   if (VTUNERUNNING && event != NULL) {
186     switch (event->type) {
187       case v8::JitCodeEvent::CODE_ADDED: {
188         std::unique_ptr<char[]> temp_file_name;
189         std::string temp_method_name = GetFunctionNameFromMixedName(
190             event->name.str, static_cast<int>(event->name.len));
191         std::vector<LineNumberInfo> jmethod_line_number_table;
192         iJIT_Method_Load jmethod;
193         memset(&jmethod, 0, sizeof jmethod);
194         jmethod.method_id = iJIT_GetNewMethodID();
195         jmethod.method_load_address = event->code_start;
196         jmethod.method_size = static_cast<unsigned int>(event->code_len);
197         jmethod.method_name = const_cast<char*>(temp_method_name.c_str());
198 
199         Local<UnboundScript> script = event->script;
200 
201         if (*script != NULL) {
202           // Get the source file name and set it to jmethod.source_file_name
203           if ((*script->GetScriptName())->IsString()) {
204             Local<String> script_name = script->GetScriptName().As<String>();
205             temp_file_name.reset(
206                 new char[script_name->Utf8Length(event->isolate) + 1]);
207             script_name->WriteUtf8(event->isolate, temp_file_name.get());
208             jmethod.source_file_name = temp_file_name.get();
209           }
210 
211           JitInfoMap::iterator entry =
212               GetEntries()->find(event->code_start);
213           if (entry != GetEntries()->end() && IsLineInfoTagged(entry->first)) {
214             JITCodeLineInfo* line_info = UntagLineInfo(entry->second);
215             // Get the line_num_info and set it to jmethod.line_number_table
216             std::list<JITCodeLineInfo::LineNumInfo>* vtunelineinfo =
217                 line_info->GetLineNumInfo();
218 
219             jmethod.line_number_size = (unsigned int)vtunelineinfo->size();
220             jmethod_line_number_table.resize(jmethod.line_number_size);
221             jmethod.line_number_table = jmethod_line_number_table.data();
222 
223             std::list<JITCodeLineInfo::LineNumInfo>::iterator Iter;
224             int index = 0;
225             for (Iter = vtunelineinfo->begin();
226                  Iter != vtunelineinfo->end();
227                  Iter++) {
228               jmethod.line_number_table[index].Offset =
229                   static_cast<unsigned int>(Iter->pc_);
230               jmethod.line_number_table[index++].LineNumber =
231                   script->GetLineNumber(Iter->pos_) + 1;
232             }
233             GetEntries()->erase(event->code_start);
234           }
235         } else if (event->wasm_source_info != nullptr) {
236           const char* filename = event->wasm_source_info->filename;
237           size_t filename_size = event->wasm_source_info->filename_size;
238           const v8::JitCodeEvent::line_info_t* line_number_table =
239               event->wasm_source_info->line_number_table;
240           size_t line_number_table_size =
241               event->wasm_source_info->line_number_table_size;
242 
243           temp_file_name.reset(new char[filename_size + 1]);
244           memcpy(temp_file_name.get(), filename, filename_size);
245           temp_file_name[filename_size] = '\0';
246           jmethod.source_file_name = temp_file_name.get();
247 
248           jmethod.line_number_size = line_number_table_size;
249           jmethod_line_number_table.resize(jmethod.line_number_size);
250           jmethod.line_number_table = jmethod_line_number_table.data();
251 
252           for (size_t index = 0; index < line_number_table_size; ++index) {
253             jmethod.line_number_table[index].LineNumber =
254                 line_number_table[index].pos;
255             jmethod.line_number_table[index].Offset =
256                 line_number_table[index].offset;
257           }
258         }
259 
260         iJIT_NotifyEvent(iJVM_EVENT_TYPE_METHOD_LOAD_FINISHED,
261                          reinterpret_cast<void*>(&jmethod));
262         break;
263       }
264       // TODO(chunyang.dai@intel.com): code_move will be supported.
265       case v8::JitCodeEvent::CODE_MOVED:
266         break;
267       // Currently the CODE_REMOVED event is not issued.
268       case v8::JitCodeEvent::CODE_REMOVED:
269         break;
270       case v8::JitCodeEvent::CODE_ADD_LINE_POS_INFO: {
271         JITCodeLineInfo* line_info =
272             reinterpret_cast<JITCodeLineInfo*>(event->user_data);
273         if (line_info != NULL) {
274           line_info->SetPosition(static_cast<intptr_t>(event->line_info.offset),
275                                  static_cast<int>(event->line_info.pos));
276         }
277         break;
278       }
279       case v8::JitCodeEvent::CODE_START_LINE_INFO_RECORDING: {
280         v8::JitCodeEvent* temp_event = const_cast<v8::JitCodeEvent*>(event);
281         temp_event->user_data = new JITCodeLineInfo();
282         break;
283       }
284       case v8::JitCodeEvent::CODE_END_LINE_INFO_RECORDING: {
285         GetEntries()->insert(std::pair <void*, void*>(event->code_start, event->user_data));
286         break;
287       }
288       default:
289         break;
290     }
291   }
292   return;
293 }
294 
295 }  // namespace internal
296 
GetVtuneCodeEventHandler()297 v8::JitCodeEventHandler GetVtuneCodeEventHandler() {
298   v8::V8::SetFlagsFromString("--no-compact-code-space");
299   return vTune::internal::VTUNEJITInterface::event_handler;
300 }
301 
302 }  // namespace vTune
303