practical object-oriented back-in-time debugging

27
Practical Object-Oriented Back-in-Time Debugging Adrian Lienhard, Tudor Gîrba and Oscar Nierstrasz Software Composition Group University of Bern, Switzerland

Upload: lienhard

Post on 14-Dec-2014

296 views

Category:

Technology


5 download

DESCRIPTION

 

TRANSCRIPT

Page 1: Practical Object-Oriented Back-in-Time Debugging

Practical Object-Oriented Back-in-Time Debugging

Adrian Lienhard, Tudor Gîrba and Oscar Nierstrasz

Software Composition Group University of Bern, Switzerland

Page 2: Practical Object-Oriented Back-in-Time Debugging

class Account {

Money balance;

void deposit(Money amount) {

this.balance += money;

}

...

}

Debugger call stack

....

....

....

....

..

NullPointerException >>

Page 3: Practical Object-Oriented Back-in-Time Debugging

class Company {

void pay(Money money, Person person) {

person.account().deposit(money);

}

}

class Account {

Money balance;

void deposit(Money amount) {

this.balance += money;

}

...

}

where does the

account object

come from?

Debugger call stack

...

NullPointerException >>

Page 4: Practical Object-Oriented Back-in-Time Debugging

class Company {

void pay(Money money, Person person) {

person.account().deposit(money);

}

}

class Account {

Money balance;

void deposit(Money amount) {

this.balance += money;

}

...

}

return

where does the

account object

come from?

Debugger call stack

...

NullPointerException >>

Page 5: Practical Object-Oriented Back-in-Time Debugging

class Company {

void pay(Money money, Person person) {

person.account().deposit(money);

}

}

class Account {

Money balance;

void deposit(Money amount) {

this.balance += money;

}

...

}

returnfield read

where does the

account object

come from?

Debugger call stack

...

NullPointerException >>

Page 6: Practical Object-Oriented Back-in-Time Debugging

class Company {

void pay(Money money, Person person) {

person.account().deposit(money);

}

}

class Person {

void createAccount(Bank bank) {

this.account = bank.openAccount();

}

}

class Account {

Money balance;

void deposit(Money amount) {

this.balance += money;

}

...

}

returnfield readfield writereturn

where does the

account object

come from?

Debugger call stack

...

Page 7: Practical Object-Oriented Back-in-Time Debugging

class Company {

void pay(Money money, Person person) {

person.account().deposit(money);

}

}

class Bank {

Account openAccount() {

return new Account();

}

}

class Person {

void createAccount(Bank bank) {

this.account = bank.openAccount();

}

}

class Account {

Money balance;

void deposit(Money amount) {

this.balance += money;

}

...

}

returnfield readfield writereturnallocation

where does the

account object

come from?

Debugger call stack

...

Page 8: Practical Object-Oriented Back-in-Time Debugging

class Company {

void pay(Money money, Person person) {

person.account().deposit(money);

}

}

class Bank {

Account openAccount() {

return new Account();

}

}

class Person {

void createAccount(Bank bank) {

this.account = bank.openAccount();

}

}

class Account {

Money balance;

void deposit(Money amount) {

this.balance += money;

}

...

}

returnfield readfield writereturnallocation

where does the

account object

come from?

Debugger call stack

...

NullPointerException >>

In 50% of the cases the execution stack contains essentially no information about the bug’s cause.

[Liblit PLDI’05]

Page 9: Practical Object-Oriented Back-in-Time Debugging

class Company {

void pay(Money money, Person person) {

person.account().deposit(money);

}

}

class Bank {

Account openAccount() {

return new Account();

}

}

class Person {

void createAccount(Bank bank) {

this.account = bank.openAccount();

}

}

class Account {

Money balance;

void deposit(Money amount) {

this.balance += money;

}

...

}

returnfield readfield writereturnallocation

where does the

account object

come from?

Debugger call stack

...

NullPointerException >>

t

Challenges: amount of data execution overhead

History recorded by a back-in-time debugger

Page 10: Practical Object-Oriented Back-in-Time Debugging

ApproachesOmniscient Debugger1

Trace-oriented debugger2 (full)

+ limited memory usage

Trace-oriented debugger2 (partial)

+ complete history– loss of old history

2) Pothier etal. OOPSLA’071) Lewis AADEBUG’03

– requires extensive hardware resources

– loss of history

– overhead 100x – overhead 100x+ overhead 10x

Page 11: Practical Object-Oriented Back-in-Time Debugging

ApproachesOmniscient Debugger1

Trace-oriented debugger2 (full)

+ limited memory usage

Trace-oriented debugger2 (partial)

+ complete history– loss of old history

2) Pothier etal. OOPSLA’071) Lewis AADEBUG’03

– requires extensive hardware resources

– loss of history

– overhead 100x – overhead 100x+ overhead 10x

Best of all?

+ limited memory

+ low overhead

+ complete history

Page 12: Practical Object-Oriented Back-in-Time Debugging

ApproachesOmniscient Debugger1

Trace-oriented debugger2 (full)

+ limited memory usage

Trace-oriented debugger2 (partial)

+ complete history– loss of old history

2) Pothier etal. OOPSLA’071) Lewis AADEBUG’03

– requires extensive hardware resources

– loss of history

– overhead 100x – overhead 100x+ overhead 10x

Best of all?

+ limited memory

+ low overhead

+ relevant history

Page 13: Practical Object-Oriented Back-in-Time Debugging

Relevant questions:

At runtime delete history when it gets irrelevant

How was this object passed here?

What were the previous values of a fieldand where (call stack) were they assigned?

Only history of live objects needed!

Efficient mechanism required to identify irrelevant datapoints at runtime

Page 14: Practical Object-Oriented Back-in-Time Debugging

How we can do it

VM that models history as first-class objects

Keep history together with regular objects in memory

Use GC to efficiently delete no longer needed history

Page 15: Practical Object-Oriented Back-in-Time Debugging

First-class references

Typical model: object references as direct pointers

Object Flow VM: references are represented as alias objects

header

field_1

field_2

....

field_n

header

value

context

origin

predecessor

...

header

...

header

field_1

field_2

....

field_n

header

...

regular objects

pointer

alias

Page 16: Practical Object-Oriented Back-in-Time Debugging

Object Flow VM model

MethodInvocation

context

AliasValuevalue

caller 0..1

*

*field or arrayelement

target parameter

1

*

1

Page 17: Practical Object-Oriented Back-in-Time Debugging

Object Flow VM model

MethodInvocation

context

AliasValuevalue

origin0..1 * 0..1

0..1

predecessor

caller 0..1

*

*field or arrayelement

target parameter

1

*

1

Page 18: Practical Object-Oriented Back-in-Time Debugging

Historical object state

:Account init@t1 null

valuebalance

account = new Account() t1

Page 19: Practical Object-Oriented Back-in-Time Debugging

Historical object state

:Account field-write@t2

init@t1

17

null

predecessor

value

valuebalance

account = new Account() t1

...

account.balance = 17 t2

Page 20: Practical Object-Oriented Back-in-Time Debugging

Historical object state

:Account

field-write@t2

field-write@t3

init@t1

17

42

null

predecessor

predecessor

value

value

valuebalance

account = new Account() t1

...

account.balance = 17 t2

...

account.balance = 42 t3

Page 21: Practical Object-Oriented Back-in-Time Debugging

ObjectFlow

class Person {

void printOn(Stream stream) {

stream.print(this.account);

}

}

returnfield readfield writereturnallocation

parameterfield read

active

invocation

allocation

field-write field-read

parameter

field-read

return

return

running/completed method invocationLegend alias originalias

Page 22: Practical Object-Oriented Back-in-Time Debugging

Effect of GC

active

invocation

running/completed method invocationLegend alias originalias

garbage collection

sn

ap

sh

ot

2sn

ap

sh

ot

1

Page 23: Practical Object-Oriented Back-in-Time Debugging

Evaluation memory usage (1)

0

5e+08

1e+09

1.5e+09

2e+09

2.5e+09

0 200 400 600 800 1000 0

2e+06

4e+06

6e+06

8e+06

1e+07

#classes

Number of aliases allocated (left Y-axis)Number of aliases in memory (right Y-axis)Number of objects in memory (right Y-axis)

Page 24: Practical Object-Oriented Back-in-Time Debugging

Evaluation memory usage (2)

0

1e+06

2e+06

3e+06

4e+06

5e+06

6e+06

7e+06

0 2 4 6 8 10 12 14 16 18 0

10

20

30

40

50

%

#samples

Number of aliases allocatedNumber of aliases in memoryNumber of objects in memory

Ratio between aliases in memory and allocated

Page 25: Practical Object-Oriented Back-in-Time Debugging

Evaluation memory usage (3)

0

1e+06

2e+06

3e+06

4e+06

5e+06

6e+06

7e+06

8e+06

9e+06

1e+07

5 10 15 20 25

#requests

Number of aliases allocatedNumber of aliases in memoryNumber of objects in memory

Page 26: Practical Object-Oriented Back-in-Time Debugging

Evaluation run-time overhead

Overhead GC

Recording off 1.15 1.6%

Recording on 3.84 27.6%

Average over 6 standard Smalltalk benchmarks

Largest overhead: 6.91

Page 27: Practical Object-Oriented Back-in-Time Debugging

Conclusion

✔ Retains important history – even if old

✔ Memory consumption limited in the best case, slowly growing in the worst case

✔ Relative low run-time overhead

✖ Missing control flow dependencies