/*******************************************************************************
* Copyright (c) 2010 Freescale Semiconductor.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Serge Beauchamp (Freescale Semiconductor) - initial API and implementation
*******************************************************************************/
package com.freescale.deadlockpreventer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.LinkedList;
import java.util.Map.Entry;
import com.freescale.deadlockpreventer.Analyzer.AcquisitionOrder;
import com.freescale.deadlockpreventer.QueryService.IBundleInfo;
public class Statistics
{
HashMap<String, LockImpl> locks = new HashMap<String, LockImpl>();
StringTable stringTable = new StringTable();
public Statistics() {
synchronized(Analyzer.instance().globalOrder) {
for (ArrayList<ObjectCache.Entry<AcquisitionOrder>> list : Analyzer.instance().globalOrder.cache.values()) {
for (ObjectCache.Entry<AcquisitionOrder> entry : list) {
IncompleteLockImpl lock = new IncompleteLockImpl(entry.object, stringTable);
lock.order = entry.value;
locks.put(lock.getID(), lock);
lock.recordStackTrace(entry.value.defaultStackTrace);
}
}
for (ArrayList<ObjectCache.Entry<AcquisitionOrder>> list : Analyzer.instance().globalOrder.cache.values()) {
for (ObjectCache.Entry<AcquisitionOrder> entry : list) {
String id = Util.getUniqueIdentifier(entry.object);
LockImpl lock = locks.get(id);
for (LockInfo info : entry.value.order) {
String precedentId = getLockID(info);
LockImpl precedent = locks.get(precedentId);
if (precedent == null) {
precedent = new LockImpl(info.getLock(), stringTable);
locks.put(precedentId, precedent);
}
if (!lock.hasPrecedent(precedentId)) {
ContextImpl precedentContext = new ContextImpl(precedent, info, stringTable);
lock.addPrecedent(precedentContext);
}
if (!precedent.hasFollower(id)) {
ContextImpl followerContext;
if (info.contextsPerThread.isEmpty())
followerContext = new ContextImpl(lock, info, stringTable);
else {
Entry<String, StackTraceElement[]> context = info.contextsPerThread.entrySet().iterator().next();
followerContext = new ContextImpl(lock, context.getKey(), context.getValue(), stringTable);
}
precedent.addFollower(followerContext);
}
}
}
}
}
}
private String getLockID(LockInfo info) {
if (info.lockID == null)
info.lockID = Util.getUniqueIdentifier(info.getLock());
return info.lockID;
}
public Statistics(String[] strings) {
LinkedList<String> stack = new LinkedList<String>(Arrays.asList(strings));
initialize(stack);
}
private void initialize(LinkedList<String> stack) {
stringTable = new StringTable(stack);
int count = Integer.parseInt(stack.removeFirst());
while (count-- > 0) {
LockImpl lock = new LockImpl(stack, stringTable);
locks.put(lock.getID(), lock);
}
for (LockImpl lock : locks.values()) {
lock.completeInitialization(locks);
}
}
public Statistics(LinkedList<String> stack) {
initialize(stack);
}
public ArrayList<String> serialize() {
ArrayList<String> result = new ArrayList<String>();
// 1. serialize the string table
stringTable.serialize(result);
Collection<LockImpl> collection = locks.values();
// 2. serialize the lock count
result.add(Integer.toString(collection.size()));
// 3. serialize each lock
for (LockImpl lock : collection) {
lock.serialize(result);
}
return result;
}
static public ArrayList<String> serializeLocks(ILock[] locks) {
ArrayList<String> result = new ArrayList<String>();
// 2. serialize the lock count
result.add(Integer.toString(locks.length));
// 3. serialize each lock
for (ILock lock : locks) {
lock.serialize(result);
}
return result;
}
public ILock[] unSerializeLocks(LinkedList<String> stack) {
ArrayList<ILock> result = new ArrayList<ILock>();
int count = Integer.parseInt(stack.removeFirst());
while (count-- > 0) {
LockImpl lock = new LockImpl(stack, stringTable);
result.add(lock);
}
for (LockImpl lock : locks.values()) {
lock.completeInitialization(locks);
}
return result.toArray(new ILock[0]);
}
public IBundleInfo unSerializeBundleInfo(LinkedList<String> stack) {
String name = stack.removeFirst();
BundleInfoImpl info = new BundleInfoImpl(name, stack.toArray(new String[0]));
return info;
}
public ILock[] locks() {
return locks.values().toArray(new ILock[0]);
}
public ILock lock() {
if (locks.values().isEmpty())
return null;
return locks.values().iterator().next();
}
static int[] convert(StackTraceElement[] st, StringTable table) {
// always skip the first (in java.lang.Thread.getStackTrace), then the one in the analyzer
int i = 1;
for (; i < st.length; i++) {
String tmp = st[i].toString();
if (!tmp.startsWith(Analyzer.class.getName()))
break;
}
int offset = i;
int[] result = new int[st.length - i];
for (; i < st.length; i++) {
result[i - offset] = table.get(st[i].toString());
}
return result;
}
static String[] convert(int[] stackTrace, StringTable table) {
String[] result = new String[stackTrace.length];
for (int i = 0; i < stackTrace.length; i++)
result[i] = table.get(stackTrace[i]);
return result;
}
public ArrayList<String> serializeStringTable() {
ArrayList<String> result = new ArrayList<String>();
stringTable.serialize(result);
return result;
}
public static ArrayList<String> serializeBundleInfo(String name,
String[] array) {
ArrayList<String> result = new ArrayList<String>();
result.add(name);
result.addAll(Arrays.asList(array));
return result;
}
}
class BundleInfoImpl implements IBundleInfo {
String name;
String[] packages;
public BundleInfoImpl(String name, String[] packages) {
this.name = name;
this.packages = packages;
}
@Override
public String getName() {
return name;
}
@Override
public String[] getPackages() {
return packages;
}
}
class StringTable {
Hashtable<String, Integer> map = new Hashtable<String, Integer>();
ArrayList<String> table = new ArrayList<String>();
public StringTable(LinkedList<String> stack) {
int count = Integer.parseInt(stack.removeFirst());
int total = 0;
while (count-- > 0) {
String tmp = stack.removeFirst();
Integer index = new Integer(total++);
map.put(tmp, index);
table.add(tmp);
}
}
public StringTable() {
}
public void serialize(ArrayList<String> result) {
// 1. serialize the lock count
result.add(Integer.toString(table.size()));
for (String s: table)
result.add(s);
}
public int get(String string) {
Integer index = map.get(string);
if (index == null) {
index = new Integer(map.size());
map.put(string, index);
table.add(string);
}
return index.intValue();
}
public String get(int index) {
try {
return table.get(index);
} catch (Exception e) {
return null;
}
}
}
class IncompleteLockImpl extends LockImpl
{
public IncompleteLockImpl(Object object, StringTable stringTable) {
super(object, stringTable);
key = ObjectCache.getKey(object);
this.object = object;
}
Integer key;
Object object;
AcquisitionOrder order;
}
class LockImpl implements ILock
{
StringTable st;
int id;
int[] stackTrace = new int[0];
HashMap<Integer, ContextImpl> precedents = new HashMap<Integer, ContextImpl>();
HashMap<Integer, ContextImpl> followers = new HashMap<Integer, ContextImpl>();
public LockImpl(Object object, StringTable stringTable) {
this.st = stringTable;
id = st.get(Util.getUniqueIdentifier(object));
}
public LockImpl(StringTable stringTable) {
id = -1;
this.st = stringTable;
}
public String toString() {
return st.get(id);
}
public void recordStackTrace(StackTraceElement[] stackTrace2) {
if (stackTrace.length == 0)
stackTrace = Statistics.convert(stackTrace2, st);
}
public void completeInitialization(HashMap<String, LockImpl> locks) {
for (ContextImpl context : precedents.values()) {
context.completeInitialization(locks);
}
for (ContextImpl context : followers.values()) {
context.completeInitialization(locks);
}
}
public LockImpl(LinkedList<String> stack, StringTable stringTable) {
this.st = stringTable;
// 1. un-serialize the ID
id = Integer.parseInt(stack.removeFirst());
// 2. un-serialize the precedent count
int count = Integer.parseInt(stack.removeFirst());
// 3. un serialize each precedent
while (count-- > 0) {
ContextImpl context = new ContextImpl(stack, stringTable);
precedents.put(context.pendingLockID, context);
}
// 4. serialize the followers count
count = Integer.parseInt(stack.removeFirst());
// 5. serialize each follower
while (count-- > 0) {
ContextImpl context = new ContextImpl(stack, stringTable);
followers.put(context.pendingLockID, context);
}
// 6. un-serialize the stack trace count
count = Integer.parseInt(stack.removeFirst());
// 7. un-serialize each stack trace
stackTrace = new int[count];
for (int i = 0; i < count; i++)
stackTrace[i] = Integer.parseInt(stack.removeFirst());
}
public void serialize(ArrayList<String> result) {
// 1. serialize the ID
result.add(Integer.toString(id));
// 2. serialize the precedent count
result.add(Integer.toString(precedents.size()));
// 3. serialize each precedent
for (ContextImpl context : precedents.values()) {
context.serialize(result);
}
// 4. serialize the followers count
result.add(Integer.toString(followers.size()));
// 5. serialize each follower
for (ContextImpl context : followers.values()) {
context.serialize(result);
}
// 6. serialize the stack trace count
result.add(Integer.toString(stackTrace.length));
// 7. serialize each stack trace
for (int stack : stackTrace)
result.add(Integer.toString(stack));
}
public boolean hasPrecedent(String lockID) {
return precedents.containsKey(lockID);
}
public boolean hasFollower(String lockID) {
return followers.containsKey(lockID);
}
public void addPrecedent(ContextImpl context) {
precedents.put(st.get(context.getLock().getID()), context);
}
public void addFollower(ContextImpl context) {
followers.put(st.get(context.getLock().getID()), context);
}
@Override
public String getID() {
return st.get(id);
}
@Override
public IContext[] getPrecedents() {
return precedents.values().toArray(new IContext[0]);
}
@Override
public IContext[] getFollowers() {
return followers.values().toArray(new IContext[0]);
}
@Override
public int hashCode() {
return st.get(id).hashCode();
}
@Override
public boolean equals(Object obj) {
if (obj instanceof LockImpl)
return id == ((LockImpl) obj).id;
return false;
}
@Override
public String[] getStackTrace() {
return Statistics.convert(stackTrace, st);
}
@Override
public ArrayList<String> serialize() {
ArrayList<String> result = new ArrayList<String>();
serialize(result);
return result;
}
}
class ContextImpl implements IContext
{
StringTable st;
LockImpl lock;
int pendingLockID;
int threadId;
int[] stackTrace;
public ContextImpl(LockImpl lock, LockInfo info, StringTable stringTable) {
this.st = stringTable;
this.lock = lock;
threadId = st.get(info.threadId);
stackTrace = Statistics.convert(info.stackTrace, st);
}
public ContextImpl(LockImpl lock, String threadId, StackTraceElement[] stackTrace, StringTable stringTable) {
this.st = stringTable;
this.lock = lock;
this.threadId = st.get(threadId);
this.stackTrace = Statistics.convert(stackTrace, st);
}
public void completeInitialization(HashMap<String, LockImpl> locks) {
lock = locks.get(pendingLockID);
}
public ContextImpl(LinkedList<String> stack, StringTable stringTable) {
st = stringTable;
// 1. un- serialize the lock id
pendingLockID = Integer.parseInt(stack.removeFirst());
// 2. un-serialize the thread id
threadId = Integer.parseInt(stack.removeFirst());
// 3. un-serialize the stack trace count
int count = Integer.parseInt(stack.removeFirst());
// 4. un-serialize each stack trace
stackTrace = new int[count];
for (int i = 0; i < count; i++)
stackTrace[i] = Integer.parseInt(stack.removeFirst());
}
public void serialize(ArrayList<String> result) {
// 1. serialize the lock id
result.add(Integer.toString(st.get(lock.getID())));
// 2. serialize the thread id
result.add(Integer.toString(threadId));
// 3. serialize the stack trace count
result.add(Integer.toString(stackTrace.length));
// 3. serialize each stack trace
for (int stack : stackTrace)
result.add(Integer.toString(stack));
}
@Override
public String getThreadID() {
return st.get(threadId);
}
@Override
public String[] getStackTrace() {
return Statistics.convert(stackTrace, st);
}
@Override
public ILock getLock() {
if (lock == null) {
LockImpl tmp = new LockImpl(st);
tmp.id = pendingLockID;
return tmp;
}
return lock;
}
}