def process(self, stack, value): # We always start with native current_frame = nflxprofile_pb2.StackFrame() current_frame.function_name = "(native)" current_frame.libtype = "" processed_stack = [] current_stack = [] for frame in stack: package = self.get_package(frame) if package == current_frame.function_name or self.should_skip( frame.function_name): current_stack.append(frame) continue processed_stack.append(current_frame) current_frame = nflxprofile_pb2.StackFrame() current_frame.function_name = package current_frame.libtype = "" current_stack = [] processed_stack.append(current_frame) return super().process(processed_stack, value)
def _get_stack(nflxprofile_nodes, node_id, has_node_stack=False, pid_comm=None, **args): """Get node stack using parent pointers or predefined stack.""" inverted = args.get("inverted", False) package_name = args.get("package_name", False) stack = [] # package name, only need first node if package_name: function_name = nflxprofile_nodes[node_id].function_name if has_node_stack: # uses node stack format, can't use node's function name node_stack = nflxprofile_nodes[node_id].stack function_name = node_stack[-1].function_name sanitized_function_name = function_name.split(';')[0] function_name_arr = sanitized_function_name.split('/') for name in function_name_arr: stack_frame = nflxprofile_pb2.StackFrame() stack_frame.function_name = name stack_frame.libtype = nflxprofile_nodes[node_id].libtype stack.append(stack_frame) if inverted: return reversed(stack) return stack # has node stack calculated, returning that if has_node_stack: function_name = nflxprofile_nodes[node_id].function_name pid = nflxprofile_nodes[node_id].pid if pid_comm and pid and pid in pid_comm: function_name = pid_comm[pid] stack_frame = nflxprofile_pb2.StackFrame() stack_frame.function_name = function_name stack_frame.libtype = nflxprofile_nodes[node_id].libtype stack = [stack_frame] + list(nflxprofile_nodes[node_id].stack) if inverted: return reversed(stack) return stack # need to use parent id nflxprofile_node_id = node_id while True: nflxprofile_node = nflxprofile_nodes[nflxprofile_node_id] stack_frame = nflxprofile_pb2.StackFrame() stack_frame.function_name = nflxprofile_node.function_name stack_frame.libtype = nflxprofile_node.libtype if inverted: stack.append(stack_frame) else: stack.insert(0, stack_frame) if not nflxprofile_nodes[nflxprofile_node_id].parent: break nflxprofile_node_id = nflxprofile_node.parent return stack
def process_frame(self, frame): """Process frame.""" processed_frame = nflxprofile_pb2.StackFrame() processed_frame.CopyFrom(frame) name = frame.function_name name_parts = name.split('::') class_name = name_parts[0] class_name = class_name.split('$$')[0] if frame.libtype and frame.libtype in [ 'jit', 'inlined' ] and class_name.startswith("L"): class_name = class_name[1:] if class_name.endswith(';'): class_name = class_name[:-1] class_name = class_name.replace('/', '.') if len(name_parts) > 1: processed_frame.function_name = class_name + "::" + name_parts[1] else: processed_frame.function_name = class_name return processed_frame, FrameExtras()
def test_java_naming(self): tests = [ { 'input_name': 'Ljava/util/concurrent/Executors$RunnableAdapter;::call', 'libtype': 'jit', 'expected_name': 'java.util.concurrent.Executors$RunnableAdapter::call', }, { 'input_name': 'Ljava/util/concurrent/Executors$RunnableAdapter;::call', 'libtype': 'inlined', 'expected_name': 'java.util.concurrent.Executors$RunnableAdapter::call', }, { 'input_name': 'Ljava/util/concurrent/FutureTask;::run', 'libtype': 'inlined', 'expected_name': 'java.util.concurrent.FutureTask::run', }, { 'input_name': 'Lcom/netflix/napa/SearchService$$EnhancerBySpringCGLIB$$65a2ea77;::searchNapa', 'libtype': 'jit', 'expected_name': 'com.netflix.napa.SearchService::searchNapa', }, { 'input_name': 'Lio/grpc/stub/ServerCalls$UnaryServerCallHandler$UnaryServerCallListener;::onHalfClose', 'libtype': 'jit', 'expected_name': 'io.grpc.stub.ServerCalls$UnaryServerCallHandler$UnaryServerCallListener::onHalfClose', }, { 'input_name': 'Lcom/netflix/springboot/sso/grpcextensions/GrpcContextAspect$$Lambda$2611/1287052643;::call', # noqa: E501 'libtype': 'inlined', 'expected_name': 'com.netflix.springboot.sso.grpcextensions.GrpcContextAspect::call', }, ] jsp = flamegraph.JavaStackProcessor(None, None) for test in tests: stack_frame = nflxprofile_pb2.StackFrame() stack_frame.function_name = test['input_name'] stack_frame.libtype = test['libtype'] [processed, extras] = jsp.process_frame(stack_frame) self.assertEqual(processed.function_name, test['expected_name'])
def _generate_regular_stacks(nflxprofile_nodes, root_node_id): stacks = {} queue = [] queue.append((root_node_id, None)) nodes = {} for node in nflxprofile_nodes: nodes[node['id']] = node while queue: (nflxprofile_node_id, parent_node_id) = queue.pop(0) node = nodes[nflxprofile_node_id] stack_frame = nflxprofile_pb2.StackFrame() call_frame = node.get('callFrame', node) filename = call_frame['url'] line = call_frame['lineNumber'] column = call_frame['columnNumber'] function_name = call_frame['functionName'] or '(anonymous)' children = node.get('children', []) libtype = '' if function_name in ['(garbage collector)', '(root)'] or not filename.startswith('file://'): libtype = 'kernel' elif 'node_modules' in filename: libtype = 'user' else: libtype = 'jit' if filename: if filename.startswith('file://'): filename = filename[7:] stack_frame.file.file_name = filename if line >= 0: stack_frame.file.line = line if column >= 0: stack_frame.file.column = column stack_frame.function_name = function_name stack_frame.libtype = libtype if not parent_node_id: stacks[nflxprofile_node_id] = [stack_frame] else: stacks[nflxprofile_node_id] = stacks[parent_node_id] + [stack_frame] for child_id in children: queue.append((child_id, nflxprofile_node_id)) return stacks
def _generate_package_name_stacks(nflxprofile_nodes): stacks = {} for key in nflxprofile_nodes: nflxprofile_node = nflxprofile_nodes[key] function_name = nflxprofile_node.function_name.split(';')[0] function_name_arr = function_name.split('/') stack_arr = [] for package_name in function_name_arr: stack_frame = nflxprofile_pb2.StackFrame() stack_frame.function_name = package_name stack_frame.libtype = nflxprofile_node.libtype stack_arr.append(stack_frame) stacks[key] = stack_arr return stacks
def _generate_regular_stacks(nflxprofile_nodes, root_node_id): stacks = {} queue = [] queue.append((root_node_id, None)) while queue: (nflxprofile_node_id, parent_node_id) = queue.pop(0) nflxprofile_node = nflxprofile_nodes[nflxprofile_node_id] stack_frame = nflxprofile_pb2.StackFrame() stack_frame.function_name = nflxprofile_node.function_name stack_frame.libtype = nflxprofile_node.libtype if not parent_node_id: stacks[nflxprofile_node_id] = [stack_frame] else: stacks[nflxprofile_node_id] = stacks[parent_node_id] + [stack_frame] for child_id in nflxprofile_node.children: queue.append((child_id, nflxprofile_node_id)) return stacks
def process_frame(self, frame): """Process frame.""" processed_frame = nflxprofile_pb2.StackFrame() processed_frame.CopyFrom(frame) name = frame.function_name name = name.split('::')[0] name = name.split('$$')[0] if frame.libtype and frame.libtype == 'jit' and name.startswith("L"): name = name[1:] if name.endswith(';'): name = name[:-1] name = name.replace('/', '.') processed_frame.function_name = name return processed_frame, FrameExtras()
def test_node_naming(self): tests = [ { 'input_name': 'LazyCompile:processTicksAndRejections internal/process/task_queues.js:69', 'libtype': 'jit', 'expected_name': 'processTicksAndRejections', 'extras': { 'javascript': True, 'optimized': True, 'real_name': 'LazyCompile:processTicksAndRejections internal/process/task_queues.js:69', 'v8_jit': True, }, }, { 'input_name': 'Builtins_ArgumentsAdaptorTrampoline', 'libtype': 'user', 'expected_name': 'Builtins_ArgumentsAdaptorTrampoline', 'extras': { 'javascript': False, 'optimized': None, 'real_name': 'Builtins_ArgumentsAdaptorTrampoline', 'v8_jit': False, }, }, { 'input_name': 'LazyCompile: /apps/nodequark/etc/routes/node_modules/@netflix-internal/naql-ipc/lib/ipc/AbstractClient.js:213', # noqa: E501 'libtype': 'jit', 'expected_name': '(anonymous)', 'extras': { 'javascript': True, 'optimized': True, 'real_name': 'LazyCompile: /apps/nodequark/etc/routes/node_modules/@netflix-internal/naql-ipc/lib/ipc/AbstractClient.js:213', # noqa: E501 'v8_jit': True, }, }, { 'input_name': 'v8::internal::LoadIC::Load', 'libtype': 'user', 'expected_name': 'v8::internal::LoadIC::Load', 'extras': { 'javascript': False, 'optimized': None, 'real_name': 'v8::internal::LoadIC::Load', 'v8_jit': False, }, }, { 'input_name': 'Builtins_CEntry_Return1_DontSaveFPRegs_ArgvOnStack_NoBuiltinExit', 'libtype': 'user', 'expected_name': 'Builtins_CEntry_Return1_DontSaveFPRegs_ArgvOnStack_NoBuiltinExit', 'extras': { 'javascript': False, 'optimized': None, 'real_name': 'Builtins_CEntry_Return1_DontSaveFPRegs_ArgvOnStack_NoBuiltinExit', 'v8_jit': False, }, }, { 'input_name': 'InterpretedFunction:parseResponse /apps/nodequark/etc/routes/node_modules/restify-clients/lib/JsonClient.js:76', # noqa: E501 'libtype': 'jit', 'expected_name': 'parseResponse', 'extras': { 'javascript': True, 'optimized': False, 'real_name': 'InterpretedFunction:parseResponse /apps/nodequark/etc/routes/node_modules/restify-clients/lib/JsonClient.js:76', # noqa: E501 'v8_jit': True, }, }, { 'input_name': 'node::LibuvStreamWrap::ReadStart()::{lambda(uv_stream_s*, long, uv_buf_t const*)#2}::_FUN', # noqa: E501 'libtype': 'user', 'expected_name': 'node::LibuvStreamWrap::ReadStart()::{lambda(uv_stream_s*, long, uv_buf_t const*)#2}::_FUN', # noqa: E501 'extras': { 'javascript': False, 'optimized': None, 'real_name': 'node::LibuvStreamWrap::ReadStart()::{lambda(uv_stream_s*, long, uv_buf_t const*)#2}::_FUN', # noqa: E501 'v8_jit': False, }, }, { 'input_name': 'smp_call_function_single', 'libtype': 'kernel', 'expected_name': 'smp_call_function_single', 'extras': { 'javascript': False, 'optimized': None, 'real_name': 'smp_call_function_single', 'v8_jit': False, }, }, { 'input_name': 'LazyCompile:*get bar /home/nfsuper/foo.js:1', 'libtype': 'jit', 'expected_name': 'get bar', 'extras': { 'javascript': True, 'optimized': True, 'real_name': 'LazyCompile:*get bar /home/nfsuper/foo.js:1', 'v8_jit': True, }, }, { 'input_name': 'LazyCompile:get _hierarchy /apps/nodequark/etc/routes/node_modules/@netflix-internal/naql-core/lib/core/Query.js:65', # noqa: E501 'libtype': 'jit', 'expected_name': 'get _hierarchy', 'extras': { 'javascript': True, 'optimized': True, 'real_name': 'LazyCompile:get _hierarchy /apps/nodequark/etc/routes/node_modules/@netflix-internal/naql-core/lib/core/Query.js:65', # noqa: E501 'v8_jit': True, }, }, { 'input_name': 'LazyCompile:*a [eval]:1', 'libtype': 'jit', 'expected_name': 'a', 'extras': { 'javascript': True, 'optimized': True, 'real_name': 'LazyCompile:*a [eval]:1', 'v8_jit': True, }, }, ] jsp = flamegraph.NodeJsStackProcessor(None, None) for test in tests: print(f"testing: {test['input_name']}") stack_frame = nflxprofile_pb2.StackFrame() stack_frame.function_name = test['input_name'] stack_frame.libtype = test['libtype'] [processed, extras] = jsp.process_frame(stack_frame) self.assertEqual(processed.function_name, test['expected_name']) self.assertEqual(dict_from_frame_extras(extras), test['extras'])