Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I generate the source code to create an object I'm debugging?

Typical scenario for me:

  1. The legacy code I work on has a bug that only a client in production is having
  2. I attach a debugger and figure out how to reproduce the issue on their system given their input. But, I don't know why the error is happening yet.
  3. Now I want to write an automated test on my local system to try and reproduce then fix the bug

That last step is really hard. The input can be very complex and have a lot of data to it. Creating the input by hand (eg: P p = new P(); p.setX("x"); p.setY("x"); imagine doing this 1000 times to create the object) is very tedious and error prone. In fact you may notice there's a typo in the example I just gave.

Is there an automated way to take a field from a break point in my debugger and generate source code that would create that object, populated the same way?

The only thing I've come up with is to serialize this input (using Xstream, for example). I can save that to a file and read it back in in an automated test. This has a major problem: If the class changes in certain ways (eg: a field/getter/setter name is renamed), I won't be able to deserialize the object anymore. In other words, the tests are extremely fragile.

like image 533
Daniel Kaplan Avatar asked Jul 14 '10 22:07

Daniel Kaplan


People also ask

What are the four steps to debugging?

Isolate the source of the bug. Identify the cause of the bug. Determine a fix for the bug. Apply the fix and test it.

What is an example of debug?

In hardware development, the debugging process typically looks for hardware components that are not installed or configured correctly. For example, an engineer might run a JTAG connection test to debug connections on an integrated circuit.


1 Answers

Java standard serialisation is well know to be not very usefull when objects change their version ( content, naming of fields). Its fine for quick demo projects.

More suitable for your needs, is the approach that objetcs support your own (binary) custom serialisation:
This is not difficult, use DataOutputStream to write out all fields of an object. But now introduce versiong, by first writing out a versionId. Objects that have only one version, write out versionId 1. That way you can later, when you have to introduce a change in your objetcs, remove fields, add fields, raise the version number.

Such a ICustomSerializable will then first read out the version number from the input stream, in a readObject() method, and depending on the version Id call readVersionV1() or e.g readVersionV2().

public Interface ICustomSerializable {
  void writeObject(DataOutputStream dos);
  Object readObject(DataInputStream dis);
}

public Class Foo {
  public static final VERSION_V1 = 1;
  public static final VERSION_V2 = 2;

  public static final CURRENT_VERSION = VERSION_V2;
  private int version;

  private int fooNumber;
  private double fooDouble;

  public void writeObject(DataOutputStream dos) {
     dos.writeInt(this.version);
      if (version == VERSION_V1) {
          writeVersionV1(dos);
      } else (version == VERSION_V2) {
          writeVersionV2(dos);
      } else {
         throw new IllegalFormatException("unkown version: " + this.version);
      }
  }
  public void writeVersionV1(DataOutputStream dos) {
        writeInt(this.fooNumber);
        writeDouble(this.fooValue);
  }
}

Further getter and setter, and a constructor with initialised the version to CURRENT_VERSION is needed.

This kind of serialisazion is safe to refactoring if you change or add also the appropriate read and write version. For complex objects using classes from external libs not und your controll, it can be more work, but strings, lists are easily serialized.

like image 131
AlexWien Avatar answered Sep 25 '22 06:09

AlexWien