Skip to content

Commit

Permalink
Fix for negative memory addresses
Browse files Browse the repository at this point in the history
  • Loading branch information
aragozin committed Nov 26, 2014
1 parent 1653cf5 commit bc743e3
Show file tree
Hide file tree
Showing 13 changed files with 183 additions and 66 deletions.
16 changes: 11 additions & 5 deletions hprof-heap/src/main/java/org/gridkit/jvmtool/heapdump/RefSet.java
Expand Up @@ -40,29 +40,35 @@ public boolean get(long index) {
if (index % 8 != 0) {
throw new IllegalArgumentException("" + index);
}
return super.get(index / 8);
return super.get(index >>> 3);
}

@Override
public long seekOne(long index) {
index = (index + 7) / 8; // alligning to 8 byte boundary
return 8 * super.seekOne(index);
index = (index + 7) >>> 3; // aligning to 8 byte boundary
long result = super.seekOne(index);
if (result == -1) {
return -1;
}
else {
return 8 * result;
}
}

@Override
public boolean getAndSet(long index, boolean value) {
if (index % 8 != 0) {
throw new IllegalArgumentException("" + index);
}
return super.getAndSet(index / 8, value);
return super.getAndSet(index >>> 3, value);
}

@Override
public void set(long index, boolean value) {
if (index % 8 != 0) {
throw new IllegalArgumentException("" + index);
}
super.set(index / 8, value);
super.set(index >>> 3, value);
}

@Override
Expand Down
Expand Up @@ -78,8 +78,8 @@ public void set(long index, boolean value) {
long lindex = index / 64;
long bit = 1l << (index % 64);
if (value) {
array.set(lindex, bit | array.get(lindex));
}
array.set(lindex, bit | array.get(lindex));
}
else {
array.set(lindex, (~bit) & array.get(lindex));
}
Expand Down Expand Up @@ -208,12 +208,12 @@ public SeekerIterator(PagedBitMap bitmap) {

@Override
public boolean hasNext() {
return next >= 0;
return next != -1;
}

@Override
public Long next() {
if (next < 0) {
if (next == -1) {
throw new NoSuchElementException();
}
long n = next;
Expand Down
Expand Up @@ -30,16 +30,17 @@ class SparsePagedLongArray implements LongArray {
public final static long NULL_VALUE = 0;

protected long lastIndex = -1;
protected SortedMap<Integer, long[]> pages = new TreeMap<Integer, long[]>();
// should use long page indexes, some OSes tend to use high memory regions
protected SortedMap<Long, long[]> pages = new TreeMap<Long, long[]>();


public long get(long n) {
if (n > lastIndex) {
return NULL_VALUE;
}
int bi = (int) (n >> PAGE_BITS);
long bi = n >>> PAGE_BITS;
if (bi < 0) {
throw new ArrayIndexOutOfBoundsException(bi);
throw new ArrayIndexOutOfBoundsException("" + bi);
}
long[] page = getPageForRead(bi);
if (page == null) {
Expand All @@ -49,10 +50,10 @@ public long get(long n) {
}

public long seekNext(long start) {
int startPage = (int) (start >> PAGE_BITS);
SortedMap<Integer, long[]> pages = this.pages.tailMap(startPage);
for(Map.Entry<Integer, long[]> entry: pages.entrySet()) {
int pi = entry.getKey();
long startPage = start >> PAGE_BITS;
SortedMap<Long, long[]> pages = this.pages.tailMap(startPage);
for(Map.Entry<Long, long[]> entry: pages.entrySet()) {
long pi = entry.getKey();
long[] page = entry.getValue();
long ps = ((long)pi) << PAGE_BITS;
long pe = ps + PAGE_SIZE;
Expand All @@ -67,7 +68,7 @@ public long seekNext(long start) {

public void set(long n, long value) {
lastIndex = Math.max(lastIndex, n);
int bi = (int) (n >> PAGE_BITS);
long bi = n >>> PAGE_BITS;
long[] page = value == NULL_VALUE ? getPageForRead(bi) : getPageForWrite(bi);
if (page == null && value == NULL_VALUE) {
if (value == NULL_VALUE) {
Expand All @@ -77,12 +78,12 @@ public void set(long n, long value) {
page[(int) (n & PAGE_MASK)] = value;
}

protected long[] getPageForRead(int bi) {
protected long[] getPageForRead(long bi) {
long[] page = pages.get(bi);
return page;
}

protected long[] getPageForWrite(int bi) {
protected long[] getPageForWrite(long bi) {
long[] page = pages.get(bi);
if (page == null) {
page = new long[PAGE_SIZE];
Expand Down
Expand Up @@ -57,10 +57,11 @@ class HeapOffsetMap {
// private final int pageSizeBits = 12;
private final int pageSizeBits = 10;
private final int pageSize = 1 << pageSizeBits;
private final int allignment = 8;
private final int allignmentBits = 3;
private final int allignment = 1 << allignmentBits;
private final int pageAddessSpan = pageSize * allignment;

private long idOffset;
private long cidOffset;
private long[] offsetMap; // maps IDs to offsets
private final int[] cachePageId;
private final int[][] cachePageData;
Expand All @@ -78,7 +79,13 @@ public HeapOffsetMap(HprofHeap heap) {
TagBounds bounds = heap.getAllInstanceDumpBounds();

pointer[0] = bounds.startOffset;
idOffset = readID();
cidOffset = readID();
if (cidOffset % allignment != 0) {
throw new IllegalArgumentException("Allignment violated for " + cidOffset);
}
// avoid signed arithmetic
cidOffset = cidOffset >>> allignmentBits;

long span = bounds.endOffset - bounds.startOffset; // rough estimate
offsetMap = new long[(int)((span) / (pageAddessSpan)) + 1];
offsetMap[0] = bounds.startOffset;
Expand All @@ -88,35 +95,49 @@ public HeapOffsetMap(HprofHeap heap) {
Arrays.fill(cachePageId, -1);
}

public long offset(long id) {
if (id < idOffset ) {
public long offset(long origId) {
try {
if (origId % allignment != 0) {
throw new IllegalArgumentException("ID is not alligned: " + origId);
}
return offsetForCompressed(origId >>> allignmentBits);
}
catch(IllegalArgumentException e) {
IllegalArgumentException ee = new IllegalArgumentException(e.getMessage() + " ID: " + origId);
ee.setStackTrace(e.getStackTrace());
throw ee;
}
}

long offsetForCompressed(long cid) {
if (cid < cidOffset ) {
// this map happen if sub regions of heap are not orderer
// by physical memory addresses
while(!scanComplete) {
// try to scan forward until mathich sub region is mapped
// try to scan forward until matching sub region is mapped
scanPage(maxPage + 1);
if (id >= idOffset) {
if (cid >= cidOffset) {
break;
}
}
}
long ref = compressID(id);
long ref = compressID(cid << allignmentBits); // restore original value
int page = (int) (ref / pageSize);
if (page >= maxPage) {
if (page != scanPage(page)) {
// address span has been extended
ref = compressID(id);
ref = compressID(cid << allignmentBits); // restore original value
page = (int) (ref / pageSize);
}
}
int[] shiftMap = getPage(page);
long baseOffs = offsetMap[page];
if (baseOffs < 0) {
throw new IllegalArgumentException("ID is not valid: " + id);
throw new IllegalArgumentException("ID is not valid: " + cid);
}
int shift = shiftMap[(int) (ref % pageSize)];
if (shift < 0) {
throw new IllegalArgumentException("ID is not valid: " + id);
throw new IllegalArgumentException("ID is not valid: " + cid);
}
long offs = baseOffs + shift;
return offs;
Expand All @@ -139,11 +160,12 @@ private int scanPage(int page) {

long ptr = pointer[0];
long iid = dumpBuffer.getID(ptr + 1);
long ciid = iid >>> allignmentBits;

// number of pages to be added up front
int ps = (int) (((idOffset - iid + pageAddessSpan - 1) / (pageAddessSpan)));
long oldIdBase = idOffset;
idOffset -= ps * pageAddessSpan;
int ps = (int) (((cidOffset - ciid + pageSize - 1) / (pageSize)));
long oldIdBase = cidOffset;
cidOffset -= ps * pageSize;
long[] noffsetMap = new long[offsetMap.length + ps];
Arrays.fill(noffsetMap, 0, ps, 0); // explicitly nullify array to avoid possible JIT bug
System.arraycopy(offsetMap, 0, noffsetMap, ps, offsetMap.length);
Expand All @@ -168,7 +190,7 @@ private int scanPage(int page) {
scanComplete = true;
}
long rid = readID();
if (rid >= 0) {
if (rid != -1) {
long nid = compressID(rid);
int nn = (int) (nid / pageSize);
if (offsetMap.length <= nn) {
Expand Down Expand Up @@ -314,13 +336,13 @@ private long readID() {
}

private long compressID(long origId) {
if (idOffset > origId) {
throw new MalformedInstanceIdException("ID is below threshold (" + idOffset + "): " + origId);
}
if ((origId - idOffset) % allignment != 0) {
if (origId % allignment != 0) {
throw new IllegalArgumentException("ID is not alligned: " + origId);
}
return (origId - idOffset) / allignment;
if (cidOffset > (origId >>> allignmentBits)) {
throw new MalformedInstanceIdException("ID is below threshold (" + (cidOffset << allignmentBits) + "): " + origId);
}
return (origId >>> allignmentBits) - cidOffset;
}

public static class MalformedInstanceIdException extends IllegalArgumentException {
Expand Down
@@ -0,0 +1,64 @@
package org.netbeans.lib.profiler.heap;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;

import org.junit.Test;

/**
* Resolves offset by instance ID.
*
* @author Alexey Ragozin (alexey.ragozin@gmail.com)
*/
public class LayoutPrinter {

HprofHeap heap;
HprofByteBuffer dumpBuffer;
long[] pointer = new long[1];

public void scan(String path) throws FileNotFoundException, IOException {
FastHprofHeap heap = new FastHprofHeap(new File(path), 0);
this.heap = heap;
this.dumpBuffer = heap.dumpBuffer;

TagBounds bounds = heap.getAllInstanceDumpBounds();
pointer[0] = bounds.startOffset;
long offs = pointer[0];
long lastPtr = readID() >>> 3;
System.out.println("" + offs + " -> " + lastPtr);
while(true) {
offs = pointer[0];
long ref = readID();
if (ref == -1) {
break;
}
long ptr = ref >>> 3;
if (ptr <= lastPtr) {
System.out.println("" + offs + " -> " + lastPtr);
}
lastPtr = ptr;
}
}

@Test
public void test() throws FileNotFoundException, IOException {
scan("file path");
}

private long readID() {
TagBounds bounds = heap.getAllInstanceDumpBounds();

while(pointer[0] < bounds.endOffset) {
long ptr = pointer[0];
int tag = heap.readDumpTag(pointer);

if ( tag == HprofHeap.INSTANCE_DUMP
|| tag == HprofHeap.OBJECT_ARRAY_DUMP
|| tag == HprofHeap.PRIMITIVE_ARRAY_DUMP) {
return dumpBuffer.getID(ptr + 1);
}
}
return -1;
}
}
16 changes: 12 additions & 4 deletions sjk-core/src/main/java/org/gridkit/jvmtool/JmxConnectionInfo.java
Expand Up @@ -62,7 +62,11 @@ public MBeanServerConnection getMServer() {
}

if (pid != null) {
return AttachManager.getDetails(pid).getMBeans();
MBeanServerConnection mserver = AttachManager.getDetails(pid).getMBeans();
if (mserver == null) {
SJK.fail("Failed to access MBean server: " + pid);
}
return mserver;
}
else if (sockAddr != null) {
String host = host(sockAddr);
Expand All @@ -74,7 +78,11 @@ else if (sockAddr != null) {
}
env = Collections.singletonMap(JMXConnector.CREDENTIALS, (Object)new String[]{user, password});
}
return connectJmx(host, port, env);
MBeanServerConnection mserver = connectJmx(host, port, env);
if (mserver == null) {
SJK.fail("Failed to access MBean server: " + host + ":" + port);
}
return mserver;
}
else {
throw new UnsupportedOperationException();
Expand All @@ -90,9 +98,9 @@ private MBeanServerConnection connectJmx(String host, int port, Map<String, Obje
MBeanServerConnection mserver = conn.getMBeanServerConnection();
return mserver;
} catch (MalformedURLException e) {
SJK.fail(e.toString());
SJK.fail("JMX Connection failed: " + e.toString(), e);
} catch (IOException e) {
SJK.fail(e.toString());
SJK.fail("JMX Connection failed: " + e.toString(), e);
}
return null;
}
Expand Down

0 comments on commit bc743e3

Please sign in to comment.