Skip to content

Commit

Permalink
looking for top member values
Browse files Browse the repository at this point in the history
  • Loading branch information
GSmorkalov committed Jun 29, 2020
1 parent 1fecdac commit 815def8
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 24 deletions.
69 changes: 60 additions & 9 deletions README.md
Expand Up @@ -42,21 +42,72 @@ topleaked <filename> [<options>...]

Options are
```
-n --size how many items from top should be printed
-o --output use 'gdb' for passing output to gdb stdin, 'human' [default] for human readable output
-s --offset start from position s, use it to offset gcore
-l --limit max number of 8byte words to read
-t --time print processing time
-f --find find pattern
-a --around szie of context of find
-h --help This help information.
```
-n --size how many items from top should be printed
-o --output use 'gdb' for passing output to gdb stdin, 'human' [default] for human readable output
-s --offset start from position s, use it to offset gcore
-l --limit max number of 8byte words to read
-t --time print processing time
-f --find find pattern
--memberOffset offset of member starting from found valur passed by -f
--memberType size of member starting from found valur passed by -f, may be uint8, uint16, uint32, uint64
-h --help This help information.```

To process hex from output into classes or functions from your code pass output to gdb
```
topleaked myapp.core -o gdb | gdb myapp myapp.core
```

## Looking for most recent values of most recent types
After usual launch of topleaked you have information about types of leaked objects (usually because of vtbl ptr). If it is not enough you can look for most frequent values of member of found classes. To do so pass ptr from top to -f, offset of member (relative to found ptr, ussually it is vtbl so you can pass just offsetof) to --memberOffset and type (actually size) of member tp --memberSize.
For example:
```
struct Base {
virtual void foo() = 0;
};
struct Der : Base {
size_t a = 15;
void foo() override {
}
};
int main()
{
for (size_t i = 0; i < 10000; ++i) {
new Der;
}
auto d = new Der;
cout << offsetof(Der, a) << endl;
abort();
return 0;
}
```
This program prints offset of Det::a which is 8. Make core and analize it with topleaked:
```
topleaked my_core.core
0x0000000000000000 : 50124
0x000000000000000f : 10005
0x0000000000000021 : 10004
0x000055697c45cd78 : 10002
0x0000000000000002 : 195
0x0000000000000001 : 182
0x00007fe9cbd6c000 : 167
0x0000000000000008 : 161
0x00007fe9cbd5e438 : 154
0x0000000000001000 : 112
```
0x000055697c45cd78 is vtbl ptr of Der. No we can find values of Der::a with
```
topleaked my_.core -f0x55697c45cd78 --memberOffset=8 --memberType=uint64
```
Result:
```
0x000000000000000f : 10001
0x000055697ccaa080 : 1
```


# Known issues
Do not use 'gcore' for getting core dump on Linux. There are some format or alignment issues and results can not be interpreted as valid symbols. Try --offset 4 or some other to solve this problem. Or use SIGABRT (kill -SIGABRT <pid>) for dumping memory.

Expand Down
44 changes: 29 additions & 15 deletions source/app.d
Expand Up @@ -5,9 +5,12 @@ import std.getopt;
import std.bitmanip;
import std.datetime.stopwatch;
import core.stdc.stdint;
import std.conv;

enum Format {human, gdb}

enum MemberType {uint8, uint16, uint32, uint64}

string usage = "Usage:\n topleaked <filename> [<options>...]";

void main(string[] args) {
Expand All @@ -16,8 +19,10 @@ void main(string[] args) {
bool time = false;
size_t offset;
size_t limit = size_t.max;
string patternStr;
uint64_t pattern;
size_t around;
size_t memberOffset;
MemberType memberType;
try {
auto opts = getopt(
args,
Expand All @@ -26,13 +31,20 @@ void main(string[] args) {
"offset|s", "start from position s, use it to offset gcore", &offset,
"limit|l", "max number of 8byte words to read", &limit,
"time|t", "print processing time", &time,
"find|f", "find pattern", &pattern,
"around|a", "szie of context of find", & around
"find|f", "find pattern", &patternStr,
"memberOffset", "offset of member starting from found valur passed by -f", &memberOffset,
"memberType", "size of member starting from found valur passed by -f, may be uint8, uint16, uint32, uint64", &memberType,
);
if (opts.helpWanted) {
defaultGetoptPrinter("Some information about the program.", opts.options);
return;
}
if (!patternStr.empty) {
if (patternStr.startsWith("0x")) {
patternStr = patternStr[2..$];
}
pattern = parse!uint64_t(patternStr, 16);
}
} catch (Exception e) {
stderr.writeln(e.msg);
return;
Expand All @@ -48,14 +60,14 @@ void main(string[] args) {

auto sw = StopWatch(AutoStart.no);
sw.start();
if (pattern) {
auto res = readFile(name, offset, limit).findPattern(pattern, around, size);
foreach (row; res) {
foreach(v; row) {
writef("0x%016x ", v);
}
writeln();
if (patternStr) {
final switch(memberType) {
case MemberType.uint8: readFile(name, offset, limit).findMember!uint8_t(pattern, memberOffset).findMostFrequent(size).printResult(format); break;
case MemberType.uint16: readFile(name, offset, limit).findMember!uint16_t(pattern, memberOffset).findMostFrequent(size).printResult(format); break;
case MemberType.uint32: readFile(name, offset, limit).findMember!uint32_t(pattern, memberOffset).findMostFrequent(size).printResult(format); break;
case MemberType.uint64: readFile(name, offset, limit).findMember!uint64_t(pattern, memberOffset).findMostFrequent(size).printResult(format); break;
}

} else {
readFile(name, offset, limit).findMostFrequent(size).printResult(format);
}
Expand All @@ -64,14 +76,16 @@ void main(string[] args) {
if (time) writeln("Done in ", sw.peek);
}

auto findPattern(Range)(Range range, uint64_t pattern, size_t around, size_t limit) {
uint64_t[][] result;
auto findMember(Result)(uint64_t[] range, uint64_t pattern, size_t offset) {
Result[] result;
foreach (i, v; range) {
if (v == pattern) {
result ~= range[i-around..i+around+1];
if (result.length >= limit) {
byte* ptr = cast(byte*) &range[i];
ptr += offset;
if (ptr > cast(byte*) &range[$-1]) {
break;
}
result ~= *cast(Result*)ptr;
}
}
return result;
Expand Down Expand Up @@ -114,7 +128,7 @@ uint64_t read64(ubyte[] src) {
return std.bitmanip.read!(uint64_t, std.system.Endian.littleEndian)(src);
}

auto findMostFrequent(Range)(Range input, size_t maxSize = 10) if (isRandomAccessRange!Range && is(ElementType!Range == uint64_t)) {
auto findMostFrequent(Range)(Range input, size_t maxSize = 10) if (isRandomAccessRange!Range) {
auto all = input.sort;
ValCount[] res = new ValCount[min(all.length, maxSize)];
return all.group.map!((p) => ValCount(p[0],p[1])).topNCopy!"a.count>b.count"(res, Yes.sortOutput);
Expand Down

0 comments on commit 815def8

Please sign in to comment.