summaryrefslogtreecommitdiff
path: root/lib/Debugger/Debugger.cpp
blob: 02c4a8a4916a03358d7b80c5d21493c1cb1513af (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
//===-- Debugger.cpp - LLVM debugger library implementation ---------------===//
// 
//                     The LLVM Compiler Infrastructure
//
// This file was developed by the LLVM research group and is distributed under
// the University of Illinois Open Source License. See LICENSE.TXT for details.
// 
//===----------------------------------------------------------------------===//
// 
// This file contains the main implementation of the LLVM debugger library.
//
//===----------------------------------------------------------------------===//

#include "llvm/Debugger/Debugger.h"
#include "llvm/Module.h"
#include "llvm/ModuleProvider.h"
#include "llvm/Bytecode/Reader.h"
#include "llvm/Debugger/InferiorProcess.h"
#include "Support/StringExtras.h"
using namespace llvm;

/// Debugger constructor - Initialize the debugger to its initial, empty, state.
///
Debugger::Debugger() : Environment(0), Program(0), Process(0) {
}

Debugger::~Debugger() {
  // Killing the program could throw an exception.  We don't want to progagate
  // the exception out of our destructor though.
  try {
    killProgram();
  } catch (const char *) {
  } catch (const std::string &) {
  }
  
  unloadProgram();
}

/// getProgramPath - Get the path of the currently loaded program, or an
/// empty string if none is loaded.
std::string Debugger::getProgramPath() const {
  return Program ? Program->getModuleIdentifier() : "";
}

static Module *
getMaterializedModuleProvider(const std::string &Filename) {
  try {
    std::auto_ptr<ModuleProvider> Result(getBytecodeModuleProvider(Filename));
    if (!Result.get()) return 0;
  
    Result->materializeModule();
    return Result.release()->releaseModule();
  } catch (...) {
    return 0;
  }
}

/// loadProgram - If a program is currently loaded, unload it.  Then search
/// the PATH for the specified program, loading it when found.  If the
/// specified program cannot be found, an exception is thrown to indicate the
/// error.
void Debugger::loadProgram(const std::string &Filename) {
  if ((Program = getMaterializedModuleProvider(Filename)) ||
      (Program = getMaterializedModuleProvider(Filename+".bc")))
    return;   // Successfully loaded the program.

  // Search the program path for the file...
  if (const char *PathS = getenv("PATH")) {
    std::string Path = PathS;

    std::string Directory = getToken(Path, ":");
    while (!Directory.empty()) {
      if ((Program = getMaterializedModuleProvider(Directory +"/"+ Filename)) ||
          (Program = getMaterializedModuleProvider(Directory +"/"+ Filename
                                                                      + ".bc")))
        return;   // Successfully loaded the program.

      Directory = getToken(Path, ":");
    }
  }

  throw "Could not find program '" + Filename + "'!";
}

/// unloadProgram - If a program is running, kill it, then unload all traces
/// of the current program.  If no program is loaded, this method silently
/// succeeds.
void Debugger::unloadProgram() {
  if (!isProgramLoaded()) return;
  killProgram();
  delete Program;
  Program = 0;
}


/// createProgram - Create an instance of the currently loaded program,
/// killing off any existing one.  This creates the program and stops it at
/// the first possible moment.  If there is no program loaded or if there is a
/// problem starting the program, this method throws an exception.
void Debugger::createProgram() {
  if (!isProgramLoaded())
    throw "Cannot start program: none is loaded.";

  // Kill any existing program.
  killProgram();

  // Add argv[0] to the arguments vector..
  std::vector<std::string> Args(ProgramArguments);
  Args.insert(Args.begin(), getProgramPath());

  // Start the new program... this could throw if the program cannot be started.
  Process = InferiorProcess::create(Program, Args, Environment);
}

/// killProgram - If the program is currently executing, kill off the
/// process and free up any state related to the currently running program.  If
/// there is no program currently running, this just silently succeeds.
void Debugger::killProgram() {
  // The destructor takes care of the dirty work.
  try {
    delete Process;
  } catch (...) {
    Process = 0;
    throw;
  }
  Process = 0;
}

/// stepProgram - Implement the 'step' command, continuing execution until
/// the next possible stop point.
void Debugger::stepProgram() {
  assert(isProgramRunning() && "Cannot step if the program isn't running!");
  try {
    Process->stepProgram();
  } catch (InferiorProcessDead &IPD) {
    killProgram();
    throw NonErrorException("The program stopped with exit code " +
                            itostr(IPD.getExitCode()));
  } catch (...) {
    killProgram();
    throw;
  }
}

/// nextProgram - Implement the 'next' command, continuing execution until
/// the next possible stop point that is in the current function.
void Debugger::nextProgram() {
  assert(isProgramRunning() && "Cannot next if the program isn't running!");
  try {
    // This should step the process.  If the process enters a function, then it
    // should 'finish' it.  However, figuring this out is tricky.  In
    // particular, the program can do any of:
    //  0. Not change current frame.
    //  1. Entering or exiting a region within the current function
    //     (which changes the frame ID, but which we shouldn't 'finish')
    //  2. Exiting the current function (which changes the frame ID)
    //  3. Entering a function (which should be 'finish'ed)
    // For this reason, we have to be very careful about when we decide to do
    // the 'finish'.

    // Get the current frame, but don't trust it.  It could change...
    void *CurrentFrame = Process->getPreviousFrame(0);

    // Don't trust the current frame: get the caller frame.
    void *ParentFrame  = Process->getPreviousFrame(CurrentFrame);
    
    // Ok, we have some information, run the program one step.
    Process->stepProgram();

    // Where is the new frame?  The most common case, by far is that it has not
    // been modified (Case #0), in which case we don't need to do anything more.
    void *NewFrame = Process->getPreviousFrame(0);
    if (NewFrame != CurrentFrame) {
      // Ok, the frame changed.  If we are case #1, then the parent frame will
      // be identical.
      void *NewParentFrame = Process->getPreviousFrame(NewFrame);
      if (ParentFrame != NewParentFrame) {
        // Ok, now we know we aren't case #0 or #1.  Check to see if we entered
        // a new function.  If so, the parent frame will be "CurrentFrame".
        if (CurrentFrame == NewParentFrame)
          Process->finishProgram(NewFrame);
      }
    }

  } catch (InferiorProcessDead &IPD) {
    killProgram();
    throw NonErrorException("The program stopped with exit code " +
                            itostr(IPD.getExitCode()));
  } catch (...) {
    killProgram();
    throw;
  }
}

/// finishProgram - Implement the 'finish' command, continuing execution
/// until the specified frame ID returns.
void Debugger::finishProgram(void *Frame) {
  assert(isProgramRunning() && "Cannot cont if the program isn't running!");
  try {
    Process->finishProgram(Frame);
  } catch (InferiorProcessDead &IPD) {
    killProgram();
    throw NonErrorException("The program stopped with exit code " +
                            itostr(IPD.getExitCode()));
  } catch (...) {
    killProgram();
    throw;
  }
}

/// contProgram - Implement the 'cont' command, continuing execution until
/// the next breakpoint is encountered.
void Debugger::contProgram() {
  assert(isProgramRunning() && "Cannot cont if the program isn't running!");
  try {
    Process->contProgram();
  } catch (InferiorProcessDead &IPD) {
    killProgram();
    throw NonErrorException("The program stopped with exit code " +
                            itostr(IPD.getExitCode()));
  } catch (...) {
    killProgram();
    throw;
  }
}