~ben-hutchings/ensoft-sextant/file-names

« back to all changes in this revision

Viewing changes to src/sextant/update_db.py

  • Committer: Tarmac
  • Author(s): Ben Hutchings
  • Date: 2014-10-23 13:01:08 UTC
  • mfrom: (29.1.33 csv-upload)
  • Revision ID: tarmac-20141023130108-696ii2di9conro6d
Programs now upload by first being parsed into csv files, then uploaded from these to the database. This is _significantly_ faster for large programs.

Furthermore, the structure of the program nodes in the database has been changed - whereas before they were unlabelled nodes with type 'program', they are now associated with the 'program' label (the database partitions on label - so this labelling keeps programs distinct from the functions). All queries in sextant have been updated to reflect this.

New module sshmanager handles the ssh connection to the database server.
New module csvwriter deals with the nuts and bolts of the csv files.

Show diffs side-by-side

added added

removed removed

Lines of Context:
5
5
# -----------------------------------------
6
6
# Given a program file to upload, or a program name to delete from the server, does the right thing.
7
7
 
 
8
from __future__ import print_function
 
9
 
8
10
__all__ = ("upload_program", "delete_program")
9
11
 
10
 
from .db_api import SextantConnection, Validator
11
 
from .objdump_parser import get_parsed_objects
 
12
from .db_api import SextantConnection
 
13
from .sshmanager import SSHConnectionError
 
14
from .objdump_parser import Parser, run_objdump
12
15
from os import path
 
16
from time import time
 
17
import subprocess
 
18
import sys
13
19
 
14
20
import logging
15
21
 
16
 
 
17
 
def upload_program(user_name, file_path, db_url, display_url='',
18
 
                   alternative_name=None, not_object_file=False):
19
 
    """
20
 
    Uploads a program to the remote database.
21
 
 
22
 
    Raises requests.exceptions.ConnectionError if the server didn't exist.
23
 
    Raises IOError if file_path doesn't correspond to a file.
24
 
    Raises ValueError if the desired alternative_name (or the default, if no
25
 
    alternative_name was specified) already exists in the database.
26
 
    :param file_path: the path to the local file we wish to upload
27
 
    :param db_url: the URL of the database (eg. http://localhost:7474)
28
 
    :param display_url: alternative URL to display instead of db_url
29
 
    :param alternative_name: a name to give the program to override the default
30
 
    :param object_file: bool(the file is an objdump text output file, rather than a compiled binary)
31
 
 
32
 
    """
33
 
 
34
 
    if not display_url:
35
 
        display_url = db_url
36
 
 
37
 
    # if no name is specified, use the form "<username>-<binary name>"
38
 
    name = alternative_name or (user_name + '-' + path.split(file_path)[-1])
39
 
 
40
 
    connection = SextantConnection(db_url)
41
 
 
42
 
    program_names = connection.get_program_names()
43
 
    if Validator.sanitise(name) in program_names:
44
 
        raise ValueError("There is already a program with name {}; "
45
 
                         "please delete the previous one with the same name "
46
 
                         "and retry, or rename the input file.".format(name))
47
 
 
48
 
    parsed_objects = get_parsed_objects(filepath=file_path,
49
 
                                        sections_to_view=['.text'],
50
 
                                        not_object_file=not_object_file,
51
 
                                        ignore_function_pointers=False)
52
 
 
53
 
    logging.info('Objdump has parsed!')
54
 
 
55
 
    program_representation = connection.new_program(Validator.sanitise(name))
56
 
 
57
 
    for obj in parsed_objects:
58
 
        for called in obj.what_do_i_call:
59
 
            if not program_representation.add_function_call(obj.name, called[-1]): # called is a tuple (address, name)
60
 
                logging.error('Validation error: {} calling {}'.format(obj.name, called[-1]))
61
 
 
62
 
    logging.info('Sending {} named objects to server {}...'.format(len(parsed_objects), display_url))
63
 
    program_representation.commit()
64
 
    logging.info('Successfully added {}.'.format(name))
65
 
 
66
 
 
67
 
def delete_program(program_name, db_url):
68
 
    """
69
 
    Deletes a program with the specified name from the database.
70
 
    :param program_name: the name of the program to delete
71
 
    :param db_url: the URL of the database (eg. http://localhost:7474)
72
 
    :return: bool(success)
73
 
    """
74
 
    connection = SextantConnection(db_url)
 
22
def upload_program(connection, user_name, file_path, program_name=None, 
 
23
                   not_object_file=False):
 
24
    """
 
25
    Upload a program's functions and call graph to the database.
 
26
 
 
27
    Arguments:
 
28
        connection:
 
29
            The SextantConnection object that manages the database connection.
 
30
        user_name:
 
31
            The user name of the user uploading the program.
 
32
        file_path:
 
33
            The path to either: the output of objdump (if not_object_file is 
 
34
            True) OR to a binary file if (not_object_file is False).
 
35
        program_name:
 
36
            An optional name to give the program in the database, if not
 
37
            specified then <user_name>-<file name> will be used.
 
38
        not_object_file:
 
39
            Flag controlling whether file_path is pointing to a dump file or
 
40
            a binary file.
 
41
    """
 
42
    if not connection._ssh:
 
43
        raise SSHConnectionError('An SSH connection is required for '
 
44
                                 'program upload.')
 
45
 
 
46
    if not program_name:
 
47
        file_no_ext = path.basename(file_path).split('.')[0]
 
48
        program_name = '{}-{}'.format(user_name, file_no_ext)
 
49
 
 
50
 
 
51
    if program_name in connection.get_program_names():
 
52
        raise ValueError('A program with name `{}` already exists in the database'
 
53
                         .format(program_name))
 
54
 
 
55
 
 
56
    print('Uploading `{}` to the database. '
 
57
          'This may take some time for larger programs.'
 
58
          .format(program_name))
 
59
    start = time()
 
60
 
 
61
    if not not_object_file:
 
62
        print('Generating dump file...', end='')
 
63
        sys.stdout.flush()
 
64
        file_path, file_object = run_objdump(file_path)
 
65
        print('done.')
 
66
    else:
 
67
        file_object = None
 
68
 
 
69
    # Make parser and wire to DBprogram.
 
70
    with connection.new_program(program_name) as program:
 
71
 
 
72
        def start_parser(program):
 
73
            print('Parsing dump file...', end='')
 
74
            sys.stdout.flush()
 
75
 
 
76
        def finish_parser(parser, program):
 
77
            # Callback to make sure the program's csv files are flushed when
 
78
            # the parser completes.
 
79
            program.func_writer.finish()
 
80
            program.call_writer.finish()
 
81
 
 
82
            print('done: {} functions and {} calls.'
 
83
                  .format(parser.function_count, parser.call_count))
 
84
 
 
85
        parser = Parser(file_path = file_path, file_object = file_object,
 
86
                        sections=[],
 
87
                        add_function = program.add_function,
 
88
                        add_call = program.add_call,
 
89
                        started=lambda parser: start_parser(program),
 
90
                        finished=lambda parser: finish_parser(parser, program))
 
91
        parser.parse()
 
92
 
 
93
        program.commit()
 
94
 
 
95
    end = time()
 
96
    print('Finished in {:.2f}s.'.format(end-start))
 
97
 
 
98
 
 
99
def delete_program(connection, program_name):
 
100
    """
 
101
    Remove the specified program from the database.
 
102
 
 
103
    Arguments:
 
104
        connection:
 
105
            The SextantConnection object managing the database connection.
 
106
        program_name:
 
107
            The name of the program to remove from the database.
 
108
    """
75
109
    connection.delete_program(program_name)
76
 
    print('Successfully deleted {}.'.format(program_name))
 
110