Merge lp:~ben-hutchings/ensoft-sextant/wierd-names-clean into lp:ensoft-sextant

Proposed by Ben Hutchings
Status: Merged
Approved by: Robert
Approved revision: 69
Merged at revision: 36
Proposed branch: lp:~ben-hutchings/ensoft-sextant/wierd-names-clean
Merge into: lp:ensoft-sextant
Prerequisite: lp:~ben-hutchings/ensoft-sextant/rel-merge
Diff against target: 474 lines (+190/-48)
10 files modified
src/sextant/db_api.py (+7/-3)
src/sextant/objdump_parser.py (+22/-8)
src/sextant/test_all.py (+39/-0)
src/sextant/test_all.sh (+0/-4)
src/sextant/test_db.py (+39/-13)
src/sextant/test_parser.py (+36/-10)
src/sextant/test_resources/parser_file2.c (+24/-0)
src/sextant/test_resources/parser_header.h (+10/-0)
src/sextant/test_resources/parser_test.c (+3/-9)
src/sextant/test_sshmanager.py (+10/-1)
To merge this branch: bzr merge lp:~ben-hutchings/ensoft-sextant/wierd-names-clean
Reviewer Review Type Date Requested Status
Robert Approve
Review via email: mp+242752@code.launchpad.net

This proposal supersedes a proposal from 2014-11-25.

Commit message

Function names are now cleaned up by a helper function, which removes __be_ prefixes if they are there (bi-endian builds) and converts names like <name>.<other stuff> to just <name>.

Tests extended to check that this works.

Description of the change

Function names are now cleaned up by a helper function, which removes __be_ prefixes if they are there (bi-endian builds) and converts names like <name>.<other stuff> to just <name>.

Tests extended to check that this works.

To post a comment you must log in.
64. By Ben Hutchings

merge from rel-merge markups

65. By Ben Hutchings

extended tests, replaced test_all.sh with test_all.py which also generates code coverage reports for the tested modeuls

66. By Ben Hutchings

whitespace fix

67. By Ben Hutchings

fixed bug with get_all_functions_calling

68. By Ben Hutchings

very minor change to the default objdump_parser print output format to include more information added since this was originally written

69. By Ben Hutchings

doc comment fix

Revision history for this message
Robert (rjwills) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/sextant/db_api.py'
2--- src/sextant/db_api.py 2014-11-25 11:40:03 +0000
3+++ src/sextant/db_api.py 2014-12-12 11:30:28 +0000
4@@ -996,7 +996,7 @@
5
6 return self._execute_query(program_name, q)
7
8- def get_all_functions_calling(self, program_name, function_called,
9+ def get_all_functions_calling(self, program_name, function_calling,
10 limit_internal=False, max_depth=1):
11 """
12 Return functions calling the specified functions.
13@@ -1014,12 +1014,16 @@
14 program_name:
15 The name of the program to query.
16
17- function_called:
18+ function_calling:
19 A string of form <name_match>:<file_match>, where at least
20 one of name_match and file_match is provided, and each may be a
21 comma separated list of strings containing wildcard '.*'
22 sequences. Specifies the list of functions to match.
23
24+ NOTE: the name is a bit of a hack to work with the javascript.
25+ Should really be function_called but that would require more
26+ js fiddling.
27+
28 limit_internal:
29 If true, only explore internal calls.
30
31@@ -1034,7 +1038,7 @@
32 q = (' MATCH (p:program {{name: "{}"}})-[:subject]->(g:func) {}'
33 ' MATCH (f)-[{}*0..{}]->(g)'
34 ' RETURN distinct f, g')
35- q = q.format(program_name, SextantConnection.get_query('g', function_called),
36+ q = q.format(program_name, SextantConnection.get_query('g', function_calling),
37 ':internal' if limit_internal else ':internal|external',
38 max_depth or '')
39
40
41=== modified file 'src/sextant/objdump_parser.py'
42--- src/sextant/objdump_parser.py 2014-11-25 11:40:03 +0000
43+++ src/sextant/objdump_parser.py 2014-12-12 11:30:28 +0000
44@@ -121,7 +121,7 @@
45 print('func {:25} {:15}{}'.format(name, typ, source))
46
47 def print_call(caller, callee, is_internal):
48- print('call {:25} {:25}'.format(caller, callee))
49+ print('call {} {:25} {:25}'.format('EI'[is_internal], caller, callee))
50
51 def print_started(parser):
52 print('parse started: {}[{}]'.format(self.path, ', '.join(self.sections)))
53@@ -199,10 +199,27 @@
54 self.add_call(caller, callee, False)
55 self._known_calls.add((caller, callee))
56 self.call_count += 1
57- print(caller, callee)
58 else:
59 self._partial_calls.add((caller, callee))
60
61+ @staticmethod
62+ def clean_id(function_identifier):
63+ """
64+ Clean the funciton identifier string.
65+ """
66+ # Bi-endian builds add a __be_ prefix to all functions,
67+ # get rid of it if it is there,
68+ if function_identifier.startswith('__be_'):
69+ function_identifier = function_identifier[len('__be_'):]
70+
71+ # Some functions look like <identifier>. or <identifier>..<digit>
72+ # - get rid of the extra bits here:
73+ if '.' in function_identifier:
74+ function_identifier = function_identifier.split('.')[0]
75+
76+ return function_identifier
77+
78+
79 def parse(self):
80 """
81 Parse self._file.
82@@ -242,10 +259,7 @@
83 # <function_name>[@plt]
84 function_identifier = line.split('<')[-1].split('>')[0]
85
86- # IOS builds add a __be_ (big endian) prefix to all functions,
87- # get rid of it if it is there,
88- if function_identifier.startswith('__be_'):
89- function_identifier = function_identifier[len('__be_'):]
90+ function_identifier = self.clean_id(function_identifier)
91
92 if '@' in function_identifier:
93 # Of form <function name>@<other stuff>.
94@@ -277,8 +291,8 @@
95 # from which we extract name
96 callee_is_ptr = False
97 function_identifier = callee_info.lstrip('<').rstrip('>\n')
98- if function_identifier.startswith('__be_'):
99- function_identifier = function_identifier[len('__be_'):]
100+
101+ function_identifier = self.clean_id(function_identifier)
102
103 if '@' in function_identifier:
104 callee = function_identifier.split('@')[0]
105
106=== added file 'src/sextant/test_all.py'
107--- src/sextant/test_all.py 1970-01-01 00:00:00 +0000
108+++ src/sextant/test_all.py 2014-12-12 11:30:28 +0000
109@@ -0,0 +1,39 @@
110+#!/usr/bin/python
111+
112+from __future__ import print_function
113+
114+import subprocess
115+import shlex
116+
117+tests = (('test_parser.py', 'objdump_parser.py'),
118+ ('test_csvwriter.py', 'csvwriter.py'),
119+ ('test_sshmanager.py', 'sshmanager.py'),
120+ ('test_db.py', 'db_api.py'))
121+
122+pycmd = 'coverage run {}'
123+covcmd = 'coverage report -m {}'
124+
125+if __name__ == '__main__':
126+ Popen = subprocess.Popen
127+
128+ for test, name in tests:
129+ print('Running tests: {}'.format(test))
130+ do_print = False
131+
132+
133+ pyproc = Popen(shlex.split(pycmd.format(test)), stdout=subprocess.PIPE)
134+ for line in pyproc.stdout:
135+ if '----------' in line or '=========' in line:
136+ do_print = True
137+
138+ if do_print:
139+ print(line.rstrip())
140+
141+ covproc = Popen(shlex.split(covcmd.format(name)))
142+ covproc.wait()
143+
144+
145+
146+
147+
148+
149
150=== removed file 'src/sextant/test_all.sh'
151--- src/sextant/test_all.sh 2014-10-13 16:01:59 +0000
152+++ src/sextant/test_all.sh 1970-01-01 00:00:00 +0000
153@@ -1,4 +0,0 @@
154-#!/usr/bin/bash
155-
156-PYTHONPATH=$PYTHONPATH:~/.
157-python -m unittest discover --pattern=test_*.py
158
159=== modified file 'src/sextant/test_db.py'
160--- src/sextant/test_db.py 2014-11-25 11:40:03 +0000
161+++ src/sextant/test_db.py 2014-12-12 11:30:28 +0000
162@@ -12,7 +12,7 @@
163 import update_db
164
165 PNAME = 'tester-parser_test'
166-NORMAL = {'main', 'normal', 'wierd$name', 'duplicates'}
167+NORMAL = {'main', 'normal', 'wierd$name', 'duplicates', 'name', 'puts'}
168
169
170 class TestFunctionQueryResults(unittest.TestCase):
171@@ -37,10 +37,12 @@
172 names = get_names(PNAME)
173 # Test file wildcard search
174 parser_names = get_names(PNAME, search=':.*parser_test.c')
175+ file2_names = get_names(PNAME, search=':.*parser_file2.c')
176
177 self.assertTrue(names.issuperset(NORMAL))
178- self.assertEquals(len(names), 24)
179- self.assertEquals(parser_names, {u'main', u'normal', u'duplicates', u'wierd$name'})
180+ self.assertEquals(len(names), 27)
181+ self.assertEquals(parser_names, {u'main', u'normal', u'duplicates'})
182+ self.assertEquals(file2_names, {u'wierd$name', u'name'})
183
184 # Test the wildcard matching
185 search = self.connection.get_function_names(PNAME, search='.*libc.*')
186@@ -58,13 +60,12 @@
187 def test_get_all_functions_called(self):
188 get_fns = self.connection.get_all_functions_called
189
190- for depth, num in zip([0, 1, 2, 3], [8, 3, 8, 8]):
191+ for depth, num in zip(range(5), [11, 3, 9, 10, 11]):
192 result = get_fns(PNAME, 'main', False, depth).functions
193- self.assertEquals(len(result), num, str(result))
194+ self.assertEquals(len(result), num)
195
196- for depth, num in zip([0, 1, 2, 3], [8, 4, 8, 8]):
197+ for depth, num in zip(range(5), [9, 6, 9, 9, 9]):
198 # Limit to internal functions
199- # TODO this isn't a great test - need greater call depth
200 result = get_fns(PNAME, 'main', True, depth).functions
201 self.assertEquals(len(result), num)
202
203@@ -80,18 +81,43 @@
204 self.assertEquals(len(result), num)
205
206 def test_get_all_paths_between(self):
207- get_paths = self.connection.get_call_paths
208-
209- result = {f.name for f in get_paths(PNAME, 'main', 'wierd$name', True, 0).functions}
210- exp = {'main', 'normal', 'duplicates', 'wierd$name'}
211- self.assertEquals(result, exp)
212+ get_paths = self.connection.get_call_paths
213+
214+ result = {f.name for f in get_paths(PNAME, 'main', 'wierd$name', False, 0).functions}
215+ exp = {'main', 'normal', 'duplicates', 'wierd$name'}
216+ self.assertEquals(result, exp)
217+
218+ self.assertFalse(get_paths(PNAME, 'main', 'wierd$name', True, 0).functions)
219
220 def test_get_shortest_paths_between(self):
221 get_paths = self.connection.get_shortest_path_between_functions
222
223- result = {f.name for f in get_paths(PNAME, 'main', 'wierd$name', True, 0).functions}
224+ result = {f.name for f in get_paths(PNAME, 'main', 'wierd$name', False, 0).functions}
225 exp = {u'main', u'normal', u'wierd$name'}
226 self.assertEquals(result, exp)
227+
228+ self.assertFalse(get_paths(PNAME, 'main', 'wierd$name', True, 0).functions)
229+
230+ def test_programs_with_metadata(self):
231+ result = self.connection.programs_with_metadata()
232+ found = False
233+
234+ for program in result:
235+ if program.program_name == PNAME:
236+ found = True
237+
238+ self.assertEquals(program.number_of_funcs, 27)
239+ self.assertEquals(program.number_of_calls, 26)
240+
241+ break
242+
243+ self.assertTrue(found)
244+
245+ def test_get_whole_program(self):
246+ result = self.connection.get_whole_program(PNAME)
247+ self.assertEquals(len(result.functions), 27)
248+
249+ self.assertFalse(self.connection.get_whole_program('no such program'))
250
251 if __name__ == '__main__':
252 unittest.main()
253
254=== modified file 'src/sextant/test_parser.py'
255--- src/sextant/test_parser.py 2014-11-20 17:25:59 +0000
256+++ src/sextant/test_parser.py 2014-12-12 11:30:28 +0000
257@@ -5,6 +5,7 @@
258
259 import objdump_parser as parser
260
261+OBJ_FILE = 'test_resources/parser_test'
262 DUMP_FILE = 'test_resources/parser_test.dump'
263
264 class TestSequence(unittest.TestCase):
265@@ -12,7 +13,8 @@
266 pass
267
268 def add_function(self, dct, name, typ, source):
269- self.assertFalse(name in dct, "duplicate function added: {} into {}".format(name, dct.keys()))
270+ self.assertFalse(name in dct, ("duplicate function added: {} into {}"
271+ .format(name, dct.keys())))
272 dct[name] = (typ, source)
273
274 def add_call(self, dct, caller, callee, is_internal):
275@@ -26,7 +28,9 @@
276 add_function = lambda n, t, s: self.add_function(functions, n, t, s)
277 add_call = lambda a, b, i: self.add_call(calls, a, b, i)
278
279- p = parser.Parser(path, sections=sections, ignore_ptrs=ignore_ptrs,
280+ file_path, file_object = parser.run_objdump(OBJ_FILE, add_file_paths=True)
281+ p = parser.Parser(file_path=None, file_object=file_object,
282+ sections=sections, ignore_ptrs=ignore_ptrs,
283 add_function=add_function, add_call=add_call)
284 res = p.parse()
285
286@@ -44,22 +48,26 @@
287 res, funcs, calls = self.do_parse()
288
289 known = 'parser_test.c'
290+ files = 'parser_file2.c'
291 unknown = 'unknown'
292
293- for name, typ, fle in zip(['normal', 'duplicates', 'wierd$name', 'printf', 'func_ptr_3'],
294- ['normal', 'normal', 'normal', 'stub', 'pointer'],
295- [known, known, known, unknown, unknown]):
296+ for name, typ, fle in zip(['normal', 'duplicates', 'wierd$name', 'printf', 'func_ptr_3',
297+ 'name', 'puts', 'inl_func'],
298+ ['normal', 'normal', 'normal', 'stub', 'pointer',
299+ 'normal', 'stub', 'normal'],
300+ [known, known, files, unknown, unknown,
301+ files, unknown, 'parser_header.h']):
302 self.assertTrue(name in funcs, "'{}' not found in function dictionary".format(name))
303 self.assertEquals(funcs[name][0], typ)
304- self.assertTrue(funcs[name][1].endswith(fle))
305+ self.assertTrue(funcs[name][1].endswith(fle), "{}-{}".format(name, fle))
306
307
308 def test_no_ptrs(self):
309 # ensure that the ignore_ptrs flags is working
310 res, funcs, calls = self.do_parse(ignore_ptrs=True)
311
312- self.assertFalse('pointer' in funcs.values())
313- self.assertEqual(len(calls['normal']), 2)
314+ self.assertFalse('pointer' in (typ for typ, src in funcs.values()))
315+ self.assertEqual(len(calls['normal']), 3)
316
317
318 def test_calls(self):
319@@ -68,7 +76,11 @@
320 self.assertTrue(('normal', True) in calls['main'])
321 self.assertTrue(('duplicates', True) in calls['main'])
322
323- normal_calls = sorted(['wierd$name', 'printf', 'func_ptr_3'])
324+ self.assertTrue(('wierd$name', False) in calls['normal'])
325+ self.assertTrue(('name', True) in calls['wierd$name'])
326+ self.assertTrue(('puts', False) in calls['name'])
327+
328+ normal_calls = sorted(['wierd$name', 'printf', 'inl_func', 'func_ptr_3'])
329 self.assertEquals(sorted(zip(*calls['normal'])[0]), normal_calls)
330
331 self.assertEquals(calls['duplicates'].count(('normal', True)), 1)
332@@ -77,6 +89,9 @@
333 self.assertTrue(('func_ptr_4', True) in calls['duplicates'])
334 self.assertTrue(('func_ptr_5', True) in calls['duplicates'])
335
336+ # A function that should only be visible in the plt section
337+ self.assertFalse('__gmon_start__' in funcs)
338+
339 def test_sections(self):
340 res, funcs, calls = self.do_parse(sections=['.plt', '.text'])
341
342@@ -84,6 +99,17 @@
343 self.assertTrue('@' not in ''.join(funcs.keys()), "check names are extracted correctly")
344 self.assertTrue('__gmon_start__' in funcs, "see a function defined only in .plt")
345
346-
347+ def test_clean_names(self):
348+ clean_id = parser.Parser.clean_id
349+ # Stripping of ios be prefixes
350+ self.assertEquals(clean_id('__be_test__be_name'), 'test__be_name')
351+ self.assertEquals(clean_id('_be_test_name'), '_be_test_name')
352+ self.assertEquals(clean_id('__betest_name'), '__betest_name')
353+
354+ # Removing of extra bits at end
355+ self.assertEquals(clean_id('test_name.'), 'test_name')
356+ self.assertEquals(clean_id('test_name..0'), 'test_name')
357+
358+
359 if __name__ == '__main__':
360 unittest.main()
361
362=== added file 'src/sextant/test_resources/parser_file2.c'
363--- src/sextant/test_resources/parser_file2.c 1970-01-01 00:00:00 +0000
364+++ src/sextant/test_resources/parser_file2.c 2014-12-12 11:30:28 +0000
365@@ -0,0 +1,24 @@
366+#include <stdio.h>
367+#include "parser_header.h"
368+
369+static int
370+wierd$name(int a);
371+
372+static int
373+__be_name(int a);
374+
375+static int
376+wierd$name(int a)
377+{
378+ a = __be_name(inl_func(a));
379+ return (a);
380+}
381+
382+static int
383+__be_name(int a)
384+{
385+ printf("In __be_name\n");
386+ return (a+1);
387+}
388+
389+
390
391=== added file 'src/sextant/test_resources/parser_header.h'
392--- src/sextant/test_resources/parser_header.h 1970-01-01 00:00:00 +0000
393+++ src/sextant/test_resources/parser_header.h 2014-12-12 11:30:28 +0000
394@@ -0,0 +1,10 @@
395+#ifndef PARSER_H
396+#define PARSER_H
397+
398+static inline int
399+inl_func(int a)
400+{
401+ return (2*a);
402+}
403+
404+#endif
405
406=== modified file 'src/sextant/test_resources/parser_test'
407Binary files src/sextant/test_resources/parser_test 2014-11-17 13:53:27 +0000 and src/sextant/test_resources/parser_test 2014-12-12 11:30:28 +0000 differ
408=== modified file 'src/sextant/test_resources/parser_test.c'
409--- src/sextant/test_resources/parser_test.c 2014-10-13 14:10:01 +0000
410+++ src/sextant/test_resources/parser_test.c 2014-12-12 11:30:28 +0000
411@@ -1,11 +1,11 @@
412 // COMMENT
413 #include<stdio.h>
414+#include "parser_header.h"
415+#include "parser_file2.c"
416
417 static int
418 normal(int a);
419
420-static int
421-wierd$name(int a);
422
423 typedef int (*pointer)(int);
424
425@@ -18,19 +18,13 @@
426 pointer ptr = wierd$name;
427
428 wierd$name(a);
429- printf("%d\n", a);
430+ printf("%d\n", inl_func(a));
431 ptr(a);
432
433 return (a);
434 }
435
436 static int
437-wierd$name(int a)
438-{
439- return (a);
440-}
441-
442-static int
443 duplicates(int a)
444 {
445 pointer ptr1 = wierd$name;
446
447=== modified file 'src/sextant/test_sshmanager.py'
448--- src/sextant/test_sshmanager.py 2014-10-17 14:35:01 +0000
449+++ src/sextant/test_sshmanager.py 2014-12-12 11:30:28 +0000
450@@ -16,7 +16,7 @@
451 self.manager = None
452
453 def get_manager(self, local_port=9643, remote_host='localhost',
454- remote_port=9643, ssh_user=None):
455+ remote_port=9643, ssh_user=None, is_localhost=False):
456 return sshmanager.SSHManager(local_port, remote_host, remote_port, ssh_user)
457
458 def test_init(self):
459@@ -34,6 +34,15 @@
460 # check connecion failure
461 self.assertRaises(sshmanager.SSHConnectionError, self.get_manager, remote_host='invalid host')
462
463+ def test_localhost(self):
464+ self.manager = self.get_manager(is_localhost=True)
465+ self.assertTrue(os.path.isdir(self.manager._tmp_dir))
466+ self.manager.close()
467+ self.assertFalse(os.path.isdir(self.manager._tmp_dir))
468+ self.manager = None
469+
470+
471+
472 def test_files(self):
473 genuine_file = 'test_resources/parser_test.c'
474 genuine_file2 = 'test_resources/parser_test'

Subscribers

People subscribed via source and target branches