blob: 7bdfeebe874c1a5478671747daa729a376ca9ea2 [file] [log] [blame]
/**********************************************************************
* Copyright (c) 2014, 2016 Ericsson, École Polytechnique de Montréal
*
* 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:
* Matthew Khouzam - Initial API and implementation
* Geneviève Bastien - Memory is per thread and only total is kept
**********************************************************************/
package org.eclipse.tracecompass.internal.lttng2.ust.core.analysis.memory;
import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.tracecompass.analysis.os.linux.core.event.aspect.LinuxTidAspect;
import org.eclipse.tracecompass.lttng2.ust.core.trace.LttngUstTrace;
import org.eclipse.tracecompass.lttng2.ust.core.trace.layout.ILttngUstEventLayout;
import org.eclipse.tracecompass.statesystem.core.ITmfStateSystemBuilder;
import org.eclipse.tracecompass.statesystem.core.exceptions.StateValueTypeException;
import org.eclipse.tracecompass.statesystem.core.exceptions.TimeRangeException;
import org.eclipse.tracecompass.statesystem.core.statevalue.ITmfStateValue;
import org.eclipse.tracecompass.statesystem.core.statevalue.TmfStateValue;
import org.eclipse.tracecompass.tmf.core.event.ITmfEvent;
import org.eclipse.tracecompass.tmf.core.event.ITmfEventField;
import org.eclipse.tracecompass.tmf.core.statesystem.AbstractTmfStateProvider;
import org.eclipse.tracecompass.tmf.core.statesystem.ITmfStateProvider;
import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
import org.eclipse.tracecompass.tmf.core.trace.TmfTraceUtils;
import com.google.common.collect.ImmutableMap;
/**
* State provider to track the memory of the threads using the UST libc wrapper
* memory events.
*
* Attribute tree:
*
* <pre>
* |- <TID number>
* | |- UST_MEMORY_MEMORY_ATTRIBUTE -> Memory Usage
* | |- UST_MEMORY_PROCNAME_ATTRIBUTE -> Process name
* </pre>
*
* @author Matthew Khouzam
* @author Geneviève Bastien
*/
public class UstMemoryStateProvider extends AbstractTmfStateProvider {
/* Version of this state provider */
private static final int VERSION = 2;
private static final Long MINUS_ONE = Long.valueOf(-1);
private static final Long ZERO = Long.valueOf(0);
private static final int MALLOC_INDEX = 1;
private static final int FREE_INDEX = 2;
private static final int CALLOC_INDEX = 3;
private static final int REALLOC_INDEX = 4;
private static final int MEMALIGN_INDEX = 5;
private static final int POSIX_MEMALIGN_INDEX = 6;
/** Map of a pointer to a memory zone to the size of the memory */
private final Map<Long, Long> fMemory = new HashMap<>();
private final @NonNull ILttngUstEventLayout fLayout;
private final @NonNull Map<String, Integer> fEventNames;
/**
* Constructor
*
* @param trace
* trace
*/
public UstMemoryStateProvider(@NonNull ITmfTrace trace) {
super(trace, "Ust:Memory"); //$NON-NLS-1$
if (!(trace instanceof LttngUstTrace)) {
fLayout = ILttngUstEventLayout.DEFAULT_LAYOUT;
} else {
fLayout = ((LttngUstTrace) trace).getEventLayout();
}
fEventNames = buildEventNames(fLayout);
}
private static @NonNull Map<String, Integer> buildEventNames(ILttngUstEventLayout layout) {
ImmutableMap.Builder<String, Integer> builder = ImmutableMap.builder();
builder.put(layout.eventLibcMalloc(), MALLOC_INDEX);
builder.put(layout.eventLibcFree(), FREE_INDEX);
builder.put(layout.eventLibcCalloc(), CALLOC_INDEX);
builder.put(layout.eventLibcRealloc(), REALLOC_INDEX);
builder.put(layout.eventLibcMemalign(), MEMALIGN_INDEX);
builder.put(layout.eventLibcPosixMemalign(), POSIX_MEMALIGN_INDEX);
return builder.build();
}
@Override
protected void eventHandle(ITmfEvent event) {
String name = event.getName();
Integer index = fEventNames.get(name);
int intIndex = (index == null ? -1 : index.intValue());
switch (intIndex) {
case MALLOC_INDEX: {
Long ptr = (Long) event.getContent().getField(fLayout.fieldPtr()).getValue();
if (ZERO.equals(ptr)) {
return;
}
Long size = (Long) event.getContent().getField(fLayout.fieldSize()).getValue();
setMem(event, ptr, size);
}
break;
case FREE_INDEX: {
Long ptr = (Long) event.getContent().getField(fLayout.fieldPtr()).getValue();
if (ZERO.equals(ptr)) {
return;
}
setMem(event, ptr, ZERO);
}
break;
case CALLOC_INDEX: {
Long ptr = (Long) event.getContent().getField(fLayout.fieldPtr()).getValue();
if (ZERO.equals(ptr)) {
return;
}
Long nmemb = (Long) event.getContent().getField(fLayout.fieldNmemb()).getValue();
Long size = (Long) event.getContent().getField(fLayout.fieldSize()).getValue();
setMem(event, ptr, size * nmemb);
}
break;
case REALLOC_INDEX: {
Long ptr = (Long) event.getContent().getField(fLayout.fieldPtr()).getValue();
if (ZERO.equals(ptr)) {
return;
}
Long newPtr = (Long) event.getContent().getField(fLayout.fieldInPtr()).getValue();
Long size = (Long) event.getContent().getField(fLayout.fieldSize()).getValue();
setMem(event, ptr, ZERO);
setMem(event, newPtr, size);
}
break;
case MEMALIGN_INDEX: {
Long ptr = (Long) event.getContent().getField(fLayout.fieldPtr()).getValue();
if (ZERO.equals(ptr)) {
return;
}
Long size = (Long) event.getContent().getField(fLayout.fieldSize()).getValue();
setMem(event, ptr, size);
}
break;
case POSIX_MEMALIGN_INDEX: {
Long ptr = (Long) event.getContent().getField(fLayout.fieldOutPtr()).getValue();
if (ZERO.equals(ptr)) {
return;
}
Long size = (Long) event.getContent().getField(fLayout.fieldSize()).getValue();
setMem(event, ptr, size);
}
break;
default:
/* Ignore other event types */
break;
}
}
@Override
public ITmfStateProvider getNewInstance() {
return new UstMemoryStateProvider(getTrace());
}
@Override
public LttngUstTrace getTrace() {
return (LttngUstTrace) super.getTrace();
}
@Override
public int getVersion() {
return VERSION;
}
private static Long getVtid(ITmfEvent event) {
/* We checked earlier that the "vtid" context is present */
Integer tid = TmfTraceUtils.resolveIntEventAspectOfClassForEvent(event.getTrace(), LinuxTidAspect.class, event);
if (tid == null) {
return MINUS_ONE;
}
return tid.longValue();
}
private @Nullable String getProcname(ITmfEvent event) {
ITmfEventField field = event.getContent().getField(fLayout.contextProcname());
if (field == null) {
return null;
}
return (String) field.getValue();
}
private void setMem(ITmfEvent event, Long ptr, Long size) {
ITmfStateSystemBuilder ss = checkNotNull(getStateSystemBuilder());
long ts = event.getTimestamp().toNanos();
Long tid = getVtid(event);
Long memoryDiff = size;
/* Size is 0, it means it was deleted */
if (ZERO.equals(size)) {
Long memSize = fMemory.remove(ptr);
if (memSize == null) {
return;
}
memoryDiff = -memSize;
} else {
fMemory.put(ptr, size);
}
try {
int tidQuark = ss.getQuarkAbsoluteAndAdd(tid.toString());
int tidMemQuark = ss.getQuarkRelativeAndAdd(tidQuark, UstMemoryStrings.UST_MEMORY_MEMORY_ATTRIBUTE);
ITmfStateValue prevMem = ss.queryOngoingState(tidMemQuark);
/* First time we set this value */
if (prevMem.isNull()) {
String procName = getProcname(event);
/*
* No tid/procname for the event for the event, added to a
* 'others' thread
*/
if (tid.equals(MINUS_ONE)) {
procName = UstMemoryStrings.OTHERS;
}
if (procName != null) {
int procNameQuark = ss.getQuarkRelativeAndAdd(tidQuark, UstMemoryStrings.UST_MEMORY_PROCNAME_ATTRIBUTE);
ss.modifyAttribute(ts, procName, procNameQuark);
}
prevMem = TmfStateValue.newValueLong(0);
}
long prevMemValue = prevMem.unboxLong();
prevMemValue += memoryDiff.longValue();
ss.modifyAttribute(ts, prevMemValue, tidMemQuark);
} catch (TimeRangeException | StateValueTypeException e) {
throw new IllegalStateException(e);
}
}
}