AtomicInteger is not atomic, and Locks don't work either in Android's framework.jar

Viewed 33

I used the ASM framework to instrument framework-minus-apex.jar in AOSP of Android 11 and 12. The instrumented code collects method invocation information and writes a trace to a file. The instrumentation is successful and the phone with the instrumented frameowork.jar can boot. The problem is, the AtomicInteger I used in the trace output class is not atomic, that is, I have duplicate value in my trace after analyzing it. The trace output class is like follows, the AtomicInteger is named _globalEventID:

package trace;

import android.os.SystemClock;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.ReentrantLock;

public class TraceRecorderBinary {
    private static int _eventLength = 18;
    private static final AtomicLong _globalEventID = new AtomicLong(0);
    protected static File _traceFile = new File("/sdcard/trace");
    private static FileOutputStream _fo = null;
    private static final int BUFSIZE = _eventLength * 1024 * 1024;
    private static byte[] _buf = new byte[BUFSIZE];
    private static int _bufCap = 0;
    private static ReentrantLock _gidLock = new ReentrantLock();
    private static ReentrantLock _timerLock = new ReentrantLock();
    private static ReentrantLock _outLock = new ReentrantLock();

    //private static long _time = System.nanoTime();

    public TraceRecorderBinary() {
        super();
        try {
            _outLock.lock();
            if (_fo == null && _traceFile.exists()) {
                _fo = new FileOutputStream(_traceFile, true);
            } else if (_fo != null && !_traceFile.exists()) {
                _fo.close();
                _fo = null;
                _bufCap = 0;
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            _outLock.unlock();
        }
    }

    protected final static long getTime() {
        /*
            Though wrapped by a lock, the time also repeats.
        */
        _timerLock.lock();
        // long ret = System.currentTimeMillis();
        long ret = SystemClock.elapsedRealtime();
        _timerLock.unlock();
        return ret;
    }

    protected final static int getGlobalID() {
        /*
            Though wrapped by a lock, the value still repeats.
        */
        _gidLock.lock();
        int ret = (int) _globalEventID.getAndIncrement();
        _gidLock.unlock();
        return ret;
    }

    private static void putBuf(byte[] bs) {
        try {
            _outLock.lock();
            if (_fo != null) {
                if (bs.length != _eventLength) {
                    return;
                }
                if (_bufCap == BUFSIZE) {
                    _fo.write(_buf);
                    _bufCap = 0;
                }

                System.arraycopy(bs, 0, _buf, _bufCap, bs.length);
                _bufCap += _eventLength;
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            _outLock.unlock();
        }
    }

    final private void putEvent(int method_id) {
        _outLock.lock();
        if (_fo == null) {
            _outLock.unlock();
            return;
        }
        _outLock.unlock();

        byte[] b = new byte[_eventLength];
        short _threadID = (short) Thread.currentThread().getId();
        int global_eid = getGlobalID();
        long time = getTime();
        
        // little-endian
        b[1] = (byte) ((_threadID >> 8) & 0xFF);
        b[0] = (byte) (_threadID & 0xFF);

        b[5] = (byte) ((method_id >> 24) & 0xff);
        b[4] = (byte) ((method_id >> 16) & 0xff);
        b[3] = (byte) ((method_id >> 8) & 0xff);
        b[2] = (byte) (method_id & 0xff);

        b[9] = (byte) ((global_eid >> 24) & 0xff);
        b[8] = (byte) ((global_eid >> 16) & 0xff);
        b[7] = (byte) ((global_eid >> 8) & 0xff);
        b[6] = (byte) (global_eid & 0xff);

        b[17] = (byte) ((time >> 56) & 0xff);
        b[16] = (byte) ((time >> 48) & 0xff);
        b[15] = (byte) ((time >> 40) & 0xff);
        b[14] = (byte) ((time >> 32) & 0xff);
        b[13] = (byte) ((time >> 24) & 0xff);
        b[12] = (byte) ((time >> 16) & 0xff);
        b[11] = (byte) ((time >> 8) & 0xff);
        b[10] = (byte) (time & 0xff);

        putBuf(b);
    }

    /*
        This method is invoked by the bytecode inserted at almost
        every method in the framework.
    */
    public void onMethodEnter(int method_id) {
        putEvent(method_id);
    }
}

Even though I used ReentrantLock, the time and gid still have duplicates.

I also tried the commented field,

private static long _time = System.nanoTime();

to see if the duplication is due to class loading or something like that, yet this _time stays the same throughout the trace.

So, any ideas? Thanks.

0 Answers
Related