LMKD 调试记录

发布于:2024-06-03 ⋅ 阅读:(85) ⋅ 点赞:(0)

frameworks/base/services/core/java/com/android/server/am/LmkdConnection.java

    /**
     * Exchange a request/reply packets with lmkd
     *
     * @param req The buffer holding the request data to be sent
     * @param repl The buffer to receive the reply
     */

   // Exchange a request/reply packets with lmkd
    public boolean exchange(ByteBuffer req, ByteBuffer repl) {
        if (repl == null) {
            return write(req);
        }

        boolean result = false;
        // set reply buffer to user-defined one to fill it
        synchronized (mReplyBufLock) {
            mReplyBuf = repl;

            if (write(req)) {
                try {
                    // wait for the reply
                    mReplyBufLock.wait();
                    result = (mReplyBuf != null);
                } catch (InterruptedException ie) {
                    result = false;
                }
            }

            // reset reply buffer
            mReplyBuf = null;
        }
        return result;
    }

//向native 层写数据

    private boolean write(ByteBuffer buf) {
        synchronized (mLmkdSocketLock) {
            try {
                mLmkdOutputStream.write(buf.array(), 0, buf.position());
            } catch (IOException ex) {
                return false;
            }
            return true;
        }
    }

//从native 中读取数据 

  private int read(ByteBuffer buf) {
        synchronized (mLmkdSocketLock) {
            try {
                return mLmkdInputStream.read(buf.array(), 0, buf.array().length);
            } catch (IOException ex) {
            }
            return -1;
        }
    }

androidu/system/memory/lmkd/lmkd.cpp

/*
 * Write the state_changed over the data socket to be propagated via AMS to statsd
 */

//将state_changed 状态通过socket传给AMS
static void stats_write_lmk_state_changed(enum lmk_state state) {
    ALOGE("stats_write_lmk_state_changed");
    LMKD_CTRL_PACKET packet_state_changed;
    const size_t len = lmkd_pack_set_state_changed(packet_state_changed, state);
    if (len == 0) {
        return;
    }
    for (int i = 0; i < MAX_DATA_CONN; i++) {
        if (data_sock[i].sock >= 0 && data_sock[i].async_event_mask & 1 << LMK_ASYNC_EVENT_STAT) {
            ctrl_data_write(i, (char*)packet_state_changed, len);
        }
    }
}

androidu/frameworks/base/services/core/java/com/android/server/am/ProcessList.java

    //向lmkd 写数据
    private static boolean writeLmkd(ByteBuffer buf, ByteBuffer repl) {
        if (!sLmkdConnection.isConnected()) {
            // try to connect immediately and then keep retrying
            sKillHandler.sendMessage(
                    sKillHandler.obtainMessage(KillHandler.LMKD_RECONNECT_MSG));

            // wait for connection retrying 3 times (up to 3 seconds)

            // 等待连接重试3次(最多3秒)
            if (!sLmkdConnection.waitForConnection(3 * LMKD_RECONNECT_DELAY_MS)) {
                return false;
            }
        }
        //调用LmkdConnection的exchange 方法
        return sLmkdConnection.exchange(buf, repl);
    }

    /**
     * Handle the unsolicited message from zygote.
     */

    //监听来自Zygoteevent的数据
    private int handleZygoteMessages(FileDescriptor fd, int events) {
        final int eventFd = fd.getInt$();
        if ((events & EVENT_INPUT) != 0) {
            // An incoming message from zygote
            try {
                final int len = Os.read(fd, mZygoteUnsolicitedMessage, 0,
                        mZygoteUnsolicitedMessage.length);
                if (len > 0 && mZygoteSigChldMessage.length == Zygote.nativeParseSigChld(
                        mZygoteUnsolicitedMessage, len, mZygoteSigChldMessage)) {
                    mAppExitInfoTracker.handleZygoteSigChld(
                            mZygoteSigChldMessage[0] /* pid */,
                            mZygoteSigChldMessage[1] /* uid */,
                            mZygoteSigChldMessage[2] /* status */);
                }
            } catch (Exception e) {
                Slog.w(TAG, "Exception in reading unsolicited zygote message: " + e);
            }
        }
        return EVENT_INPUT;
    } 

方案记录:

1.mem_free:剩余可分配内存大小,每个策略对应不同的剩余内存大小阈值

2. pgscan_kswapd:间接回收的内存页数,通过前后两次的数量差和时间差,计算出单位时间内kswpad回收的内存页数,来表示kswpad的活跃状态

3. swap_free:Swap的剩余大小,每个策略对应不同的交换分区内存大小阈值

4. pgscan_direct:直接回收的内存页数,通过前后两次的数量差和时间差,计算出单位时间内直接回收内存页数,来表示内存的压力情况

5. mem_cache

6. mem_available_kb

7. mem_total_kb

8. wmark

9 .swap_total

策略记录:

1. 当CPU 占有率超过 85% 开始杀. adj >=900

2. 使用kill进程的方法,不是forcestop 方法。

获取CPU 占用率

计算CPU 占用率:

安卓cpu信息查看与cpu占用率计算_android cpu 占用率-CSDN博客

代码:

1.androidu/system/memory/lmkd/include/lmkd.h

enum lmk_cmd {
    LMK_TARGET = 0,         /* Associate minfree with oom_adj_score */
    LMK_PROCPRIO,           /* Register a process and set its oom_adj_score */
    LMK_PROCREMOVE,         /* Unregister a process */
    LMK_PROCPURGE,          /* Purge all registered processes */
    LMK_GETKILLCNT,         /* Get number of kills */
    LMK_SUBSCRIBE,          /* Subscribe for asynchronous events */
    LMK_PROCKILL,           /* Unsolicited msg to subscribed clients on proc kills */
    LMK_UPDATE_PROPS,       /* Reinit properties */
    LMK_STAT_KILL_OCCURRED, /* Unsolicited msg to subscribed clients on proc kills for statsd log */
    LMK_STAT_STATE_CHANGED, /* Unsolicited msg to subscribed clients on state changed */
  +  LMK_STAT_LEVEL_CHANGED,
};

-#define MAX_TARGETS 6
+#define MAX_TARGETS 16

+/* LMK_LEVEL packet payload */
+struct lmk_level_data {
+       int level;
+       int64_t free_swap_kb;
+       int64_t free_mem_kb;
+       int64_t pgscan_kswapd;
+       int64_t pgscan_direct;
+       int64_t mem_cache_kb;
+       int64_t mem_available_kb;
+       int64_t mem_total_kb;
+       int64_t swap_total_kb;
+       int wmark;
+};

 

+static inline size_t lmkd_pack_set_level_changed(LMKD_CTRL_PACKET packet,struct lmk_level_data data) {
+    packet[0] = htonl(LMK_STAT_LEVEL_CHANGED);
+    packet[1] = htonl(data.level);
+    packet[2] = htonl(data.free_swap_kb);
+    packet[3] = htonl(data.free_mem_kb);
+    packet[4] = htonl(data.pgscan_kswapd);
+    packet[5] = htonl(data.pgscan_direct);
+    packet[6] = htonl(data.mem_cache_kb);
+    packet[7] = htonl(data.mem_available_kb);
+    packet[8] = htonl(data.mem_total_kb);
+    packet[9] = htonl(data.swap_total_kb);
+    packet[10] = htonl(data.wmark);
+    return 3 * sizeof(int) + 8 * sizeof(int64_t);
+}

 

2.  androidu/system/memory/lmkd/lmkd.cpp

 /* Fields to parse in /proc/meminfo */
 enum meminfo_field {
-    MI_NR_FREE_PAGES = 0,
+    MI_MEM_TOTAL = 0,
+    MI_NR_FREE_PAGES,
+    MI_MEM_AVAILABLE,

     MI_CACHED,
     MI_SWAP_CACHED,
     MI_BUFFERS,
 

 static const char* const meminfo_field_names[MI_FIELD_COUNT] = {
+    "MemTotal:",
     "MemFree:",
+    "MemAvailable:",
     "Cached:",
     "SwapCached:",
     "Buffers:",
 

 union meminfo {
     struct {
+        int64_t mem_total_kb;
         int64_t nr_free_pages;
+        int64_t nr_mem_available;
         int64_t cached;
         int64_t swap_cached;
         int64_t buffers;
@@ -770,6 +776,7 @@ static ssize_t ctrl_data_read(int dsock_idx, char* buf, size_t bufsz, struct ucr
 }

 static int ctrl_data_write(int dsock_idx, char* buf, size_t bufsz) {
+    ALOGE("ctrl_data_write");
     int ret = 0;
 
     ret = TEMP_FAILURE_RETRY(write(data_sock[dsock_idx].sock, buf, bufsz));
@@ -789,6 +796,7 @@ static int ctrl_data_write(int dsock_idx, char* buf, size_t bufsz) {
  * will receive this unsolicited notification.
  */
 static void ctrl_data_write_lmk_kill_occurred(pid_t pid, uid_t uid) {
+    ALOGE("ctrl_data_write_lmk_kill_occurred pid:%i ,uid:%i", pid,uid);
     LMKD_CTRL_PACKET packet;
     size_t len = lmkd_pack_set_prockills(packet, pid, uid);
 
@@ -799,11 +807,26 @@ static void ctrl_data_write_lmk_kill_occurred(pid_t pid, uid_t uid) {
     }
 }

+
+static void ctrl_data_write_lmk_level_changed(lmk_level_data data) {
+    ALOGE("ctrl_data_write_lmk_level_changed");
+    LMKD_CTRL_PACKET packet;
+    size_t len = lmkd_pack_set_level_changed(packet,data);
+
+    for (int i = 0; i < MAX_DATA_CONN; i++) {
+        if (data_sock[i].sock >= 0 && data_sock[i].async_event_mask & 1 << LMK_ASYNC_EVENT_KILL) {
+            ctrl_data_write(i, (char*)packet, len);
+        }
+    }
+}
+

 /*
  * Write the kill_stat/memory_stat over the data socket to be propagated via AMS to statsd
  */
 static void stats_write_lmk_kill_occurred(struct kill_stat *kill_st,
                                           struct memory_stat *mem_st) {
+    ALOGE("stats_write_lmk_kill_occurred");
     LMK_KILL_OCCURRED_PACKET packet;
     const size_t len = lmkd_pack_set_kill_occurred(packet, kill_st, mem_st);
     if (len == 0) {
@@ -830,6 +853,7 @@ static void stats_write_lmk_kill_occurred_pid(int pid, struct kill_stat *kill_st
  * Write the state_changed over the data socket to be propagated via AMS to statsd
  */
 static void stats_write_lmk_state_changed(enum lmk_state state) {
+    ALOGE("stats_write_lmk_state_changed");
     LMKD_CTRL_PACKET packet_state_changed;
     const size_t len = lmkd_pack_set_state_changed(packet_state_changed, state);
     if (len == 0) {
@@ -1473,6 +1497,7 @@ static void cmd_target(int ntargets, LMKD_CTRL_PACKET packet) {
 }
 

 static void ctrl_command_handler(int dsock_idx) {
+    ALOGE("ctrl_command_handler");
     LMKD_CTRL_PACKET packet;
     struct ucred cred;
     int len;
@@ -1492,6 +1517,7 @@ static void ctrl_command_handler(int dsock_idx) {
     }
 
     cmd = lmkd_pack_get_cmd(packet);
+    ALOGE("ctrl_command_handler command code %d", cmd);
     nargs = len / sizeof(int) - 1;
     if (nargs < 0)
         goto wronglen;
@@ -1578,6 +1604,7 @@ wronglen:
 
 static void ctrl_data_handler(int data, uint32_t events,
                               struct polling_params *poll_params __unused) {
+    ALOGI("ctrl_data_handler");
     if (events & EPOLLIN) {
 

 static void mp_event_psi(int data, uint32_t events, struct polling_params *poll_params) {
+
+    ALOGE("mp_event_psi");
 

     if (!psi_parse_mem(&psi_data)) {
         critical_stall = psi_data.mem_stats[PSI_FULL].avg10 > (float)stall_limit_critical;
     }
+
+    //上报:mem_free, swap_free, pgscan_kswpad, pgscan_direct
+    
+    ALOGE("mp_event_psi level:%s,free_swap_kb:%ld,free_mem_kb:%ld,pgscan_kswapd:%ld,pgscan_direct:%ld",level_name[level],get_free_swap(&mi) * page_k,mi.field.nr_free_pages * page_k,vs.field.pgscan_kswapd,vs.field.pgscan_direct);
+
+
+    ALOGE("mp_event_psi level:%s,page_k:%ld,mem_cache:%ld,mem_available_kb:%ld,mem_total_kb:%ld,swap_total:%ld,wmark:%d",level_name[level],page_k,mi.field.cached,mi.field.nr_mem_available,mi.field.mem_total_kb,mi.field.total_swap,wmark);
+
+     struct lmk_level_data level_data;
+     level_data.level = level;
+     level_data.free_swap_kb = get_free_swap(&mi) * page_k;
+     level_data.free_mem_kb = mi.field.nr_free_pages * page_k;
+     level_data.pgscan_kswapd = vs.field.pgscan_kswapd;
+     level_data.pgscan_direct = vs.field.pgscan_direct;
+     level_data.mem_cache_kb = mi.field.cached;
+     level_data.mem_available_kb = mi.field.nr_mem_available;
+     level_data.mem_total_kb = mi.field.mem_total_kb;
+     level_data.swap_total_kb = mi.field.total_swap;
+     level_data.wmark = wmark;
+    //if(level_changed){
+       ctrl_data_write_lmk_level_changed(level_data); 
+    //}

 

3.  androidu/frameworks/base/services/core/java/com/android/server/am/LmkdConnection.java

-    private static final String TAG = TAG_WITH_CLASS_NAME ? "LmkdConnection" : TAG_AM;
+    private static final String TAG = "LmkdConnection";

 

4.  androidu/frameworks/base/services/core/java/com/android/server/am/ProcessList.java

+import java.io.FileReader;
+import java.io.BufferedReader;
+import java.util.regex.Pattern;
+import java.util.regex.Matcher;

-    static final String TAG = TAG_WITH_CLASS_NAME ? "ProcessList" : TAG_AM;
+    static final String TAG = "ProcessList";

 

     static final byte LMK_STATE_CHANGED = 9; // Msg to subscribed clients on state changed
+    static final byte LMK_LEVEL_CHANGED = 10;

                                         LmkdStatsReporter.logStateChanged(state);
                                         return true;
+                                    case LMK_LEVEL_CHANGED:
+                                        Slog.i(TAG, "handleUnsolicitedMessage LMK_LEVEL_CHANGED");
+                                        int level = inputData.readInt();
+                                        long freeSwapKb = inputData.readInt();
+                                        long freeMemKb = inputData.readInt();
+                                        long pgscanKswapd = inputData.readInt();
+                                        long pgscanDirect = inputData.readInt();
+                                        long memCacheKb = inputData.readInt();
+                                        long memAvailableKb = inputData.readInt();
+                                        long memTotalKb = inputData.readInt();
+                                        long swapTotalKb = inputData.readInt();
+                                        long wmark = inputData.readInt();
+
+
+                                        Slog.i(TAG, "handleUnsolicitedMessage LMK_LEVEL_CHANGED level:" + level +",freeSwapKb:"+freeSwapKb +",freeMemKb:"+freeMemKb+",pgscanKswapd:"+pgscanKswapd+",pgscanDirect:"+pgscanDirect+",memCacheKb:"+memCacheKb+",memAvailableKb:"+memAvailableKb+",memTotalKb:"+memTotalKb+",swapTotalKb:"+swapTotalKb+",wmark:"+wmark);
+
+                                      Slog.i(TAG, "handleUnsolicitedMessage LMK_LEVEL_CHANGED cpu rate:" + getCPURate());
+                                    return true;

                                     default:
                                         return false;
 

+
+   private void getKillApp(){
+     //cache current running process
+     ArrayList<ProcessRecord> procs = new ArrayList<>();
+     synchronized(mService) {
+       procs.addAll(mService.mProcessList.getLruProcessesLOSP());
+     }
+
+    for (int i = 0; i < procs.size(); i++) {
+          ProcessRecord app = procs.get(i);
+          final ProcessErrorStateRecord errState = app.mErrorstate;
+          if (app.getThread() == null) {
+              Slog.w(TAG,"skip"+ app.processName + " by app.getThread( ) == null");
+              continue;
+          }
+         if (errState.isCrashing()) {
+              Slog.w(TAG,"skip" + app.processName + " by crashing");
+              continue;
+            }
+
+        if (errState.isNotResponding()) {
+            Slog.w(TAG,"skip"+ app.processName +"by NotResponding");
+            continue;
+         }
+        ApplicationInfo appInfo = app.info;
+        int appAdj = app.mstate.getSetAdj();
+        ProcessStateRecord state - app.mstate;
+        if (DEBUG) {
+             Slog.d(TAG,"checking process:" + app.processName + ",adj:" + appAdj + ", state:" + state.getCurProcState()
+               +",adjtype:"+ state.getAdjType() + ",cached:"+ state.isCached());
+          } 

+       if (appAdj < minAdj) {
+          // process adj lower than minAdj, this package should not be killed
+          skipPkgSet.add(appInfo.packageName);
+       if (killTargetMap.containsKey(appInfo.packageName)){
+            killTargetMap.remove(appInfo.packageName);
+        if (DEBUG_MORE) {
+           slog.d(TAG,"skip pkg, adj: " + appAdj + "procName:" + app.processName + "pkgname:" + appInfo.packageName);
+         }
+       continue;
+       }
+}
+
+
+
+    /**
+     * ^ 表示匹配行的开头。
+     * cpu 匹配 "cpu" 这个单词。
+     * \\s+ 匹配一个或多个空格字符。
+     * (\\d+\\s+){9} 匹配由一个或多个数字加上一个或多个空格字符组成的序列,重复9次。
+     * \\d+ 匹配一个或多个数字。
+     * $ 匹配行的结尾。 因此,整个正则表达式可以匹配以 "cpu" 开头,后面跟着10个由空格分隔的数字的行。
+     */
+private static String getCPURate() {
+        String path = "/proc/stat";// 系统CPU信息文件
+        long Totaljiffies[] = new long[2];
+        long totalIdle[] = new long[2];
+        FileReader fileReader = null;
+        BufferedReader bufferedReader = null;
+        Pattern pattern = Pattern.compile("^cpu\\s+(\\d+\\s+){9}\\d+$", Pattern.MULTILINE);
+                                                                 //正则表达式,只获取第一行
+        for (int i = 0; i < 2; i++) {  //每一次调用分为两次获取 方便求差
+            Totaljiffies[i] = 0;
+            totalIdle[i] = 0;
+            try {
+                fileReader = new FileReader(path);
+                bufferedReader = new BufferedReader(fileReader, 8192);
+                String str;
+                while ((str = bufferedReader.readLine()) != null) {  //读取stat信息
+                    if (str.toLowerCase().startsWith("cpu")) {//以cpu开头的
+                        Matcher matcher = pattern.matcher(str);//直接获取第一行cpu开头的数据
+                                                            // 不需要cpu0-7的,那样的话还得多几步运算
+                        while (matcher.find()) {
+                            String[] values = extractValues(matcher.group());
+                            Totaljiffies[i] = sumValues(values);
+                            totalIdle[i] = Long.parseLong(values[3]);
+                        }
+                    }
+                    if(i==0){//第一次获取后进行延时等待系统更新信息
+                        try {
+                            Thread.sleep(100);
+                        } catch (InterruptedException e) {
+                            e.printStackTrace();
+                        }
+                    }
+                }
+            } catch (IOException e) {
+                // TODO Auto-generated catch block
+                e.printStackTrace();
+            } finally {
+                if (bufferedReader != null) {
+                    try {
+                        bufferedReader.close();
+                    } catch (IOException e) {
+                        e.printStackTrace();
+                    }
+                }
+            }
+        }
+        double rate = 0;
+        if (Totaljiffies[1] > Totaljiffies[0] ) {//正常情况下第二次总的jiffies一定比第一次获得的数据大
+            rate = 1.0 * ((Totaljiffies[1] - totalIdle[1]) - (Totaljiffies[0] - totalIdle[0]))
+                    / (Totaljiffies[1] - Totaljiffies[0]);
+        }
+        return String.valueOf(rate);
+    }
+   /**
+     * 头行去掉cpu,合并成String[]数组 
+     * @param input
+     * @return
+     */
+    public static String[] extractValues(String input) {
+        String[] parts = input.split("\\s+");
+        String[] values = new String[parts.length - 1]; // 去掉 "cpu",所以长度减一
+
+        System.arraycopy(parts, 1, values, 0, parts.length - 1);
+        return values;
+    }
+   /**
+     * 求数组和
+     * @param input
+     * @return
+     */
+    public static Long sumValues(String input[]) {
+        Long sum = Long.valueOf(0);
+        for (String value : input) {
+            sum += Integer.parseInt(value);
+        }
+        return sum;
+    }

 

计算CPU 占有率:

    /**
     * ^ 表示匹配行的开头。
     * cpu 匹配 "cpu" 这个单词。
     * \\s+ 匹配一个或多个空格字符。
     * (\\d+\\s+){9} 匹配由一个或多个数字加上一个或多个空格字符组成的序列,重复9次。
     * \\d+ 匹配一个或多个数字。
     * $ 匹配行的结尾。 因此,整个正则表达式可以匹配以 "cpu" 开头,后面跟着10个由空格分隔的数字的行。
     */
private static String getCPURate() {
        String path = "/proc/stat";// 系统CPU信息文件
        long Totaljiffies[] = new long[2];
        long totalIdle[] = new long[2];
        FileReader fileReader = null;
        BufferedReader bufferedReader = null;
        Pattern pattern = Pattern.compile("^cpu\\s+(\\d+\\s+){9}\\d+$", Pattern.MULTILINE);
                                                                 //正则表达式,只获取第一行
        for (int i = 0; i < 2; i++) {  //每一次调用分为两次获取 方便求差
            Totaljiffies[i] = 0;
            totalIdle[i] = 0;
            try {
                fileReader = new FileReader(path);
                bufferedReader = new BufferedReader(fileReader, 8192);
                String str;
                while ((str = bufferedReader.readLine()) != null) {  //读取stat信息
                    if (str.toLowerCase().startsWith("cpu")) {//以cpu开头的
                        Matcher matcher = pattern.matcher(str);//直接获取第一行cpu开头的数据
                                                            // 不需要cpu0-7的,那样的话还得多几步运算
                        while (matcher.find()) {
                            String[] values = extractValues(matcher.group());
                            Totaljiffies[i] = sumValues(values);
                            totalIdle[i] = Long.parseLong(values[3]);
                        }
                    }
                    if(i==0){//第一次获取后进行延时等待系统更新信息
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } finally {
                if (bufferedReader != null) {
                    try {
                        bufferedReader.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
        double rate = 0;
        if (Totaljiffies[1] > Totaljiffies[0] ) {//正常情况下第二次总的jiffies一定比第一次获得的数据大
            rate = 1.0 * ((Totaljiffies[1] - totalIdle[1]) - (Totaljiffies[0] - totalIdle[0]))
                    / (Totaljiffies[1] - Totaljiffies[0]);
        }
        return String.valueOf(rate);
    }
   /**
     * 头行去掉cpu,合并成String[]数组 
     * @param input
     * @return
     */
    public static String[] extractValues(String input) {
        String[] parts = input.split("\\s+");
        String[] values = new String[parts.length - 1]; // 去掉 "cpu",所以长度减一

        System.arraycopy(parts, 1, values, 0, parts.length - 1);
        return values;
    }
   /**
     * 求数组和
     * @param input
     * @return
     */
    public static Long sumValues(String input[]) {
        Long sum = Long.valueOf(0);
        for (String value : input) {
            sum += Integer.parseInt(value);
        }
        return sum;
    }


网站公告

今日签到

点亮在社区的每一天
去签到