/* * Copyright (c) 2013-2019, Huawei Technologies Co., Ltd. All rights reserved. * Copyright (c) 2020, Huawei Device Co., Ltd. All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. * * 3. Neither the name of the copyright holder nor the names of its contributors may be used * to endorse or promote products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** * @defgroup los_vm_dump virtual memory dump operation * @ingroup kernel */ #include "los_vm_dump.h" #include "los_mmu_descriptor_v6.h" #include "fs/fs.h" #include "los_printf.h" #include "los_vm_page.h" #include "los_vm_phys.h" #include "los_process_pri.h" #include "los_atomic.h" #include "los_vm_lock.h" #include "los_memory_pri.h" #ifdef __cplusplus #if __cplusplus extern "C" { #endif /* __cplusplus */ #endif /* __cplusplus */ #define FLAG_SIZE 4 #define FLAG_START 2 //获取线性区的名称或文件路径 const CHAR *OsGetRegionNameOrFilePath(LosVmMapRegion *region) { struct file *filep = NULL; if (region == NULL) { return ""; #ifdef LOSCFG_FS_VFS } else if (LOS_IsRegionFileValid(region)) { filep = region->unTypeData.rf.file; return filep->f_path; #endif } else if (region->regionFlags & VM_MAP_REGION_FLAG_HEAP) {//堆区 return "HEAP"; } else if (region->regionFlags & VM_MAP_REGION_FLAG_STACK) {//栈区 return "STACK"; } else if (region->regionFlags & VM_MAP_REGION_FLAG_TEXT) {//文本区 return "Text"; } else if (region->regionFlags & VM_MAP_REGION_FLAG_VDSO) {//虚拟动态链接对象区(Virtual Dynamically Shared Object、VDSO) return "VDSO"; } else if (region->regionFlags & VM_MAP_REGION_FLAG_MMAP) {//映射区 return "MMAP"; } else if (region->regionFlags & VM_MAP_REGION_FLAG_SHM) {//共享区 return "SHM"; } else { return ""; } return ""; } INT32 OsRegionOverlapCheckUnlock(LosVmSpace *space, LosVmMapRegion *region) { LosVmMapRegion *regionTemp = NULL; LosRbNode *pstRbNode = NULL; LosRbNode *pstRbNodeNext = NULL; /* search the region list */ RB_SCAN_SAFE(&space->regionRbTree, pstRbNode, pstRbNodeNext) regionTemp = (LosVmMapRegion *)pstRbNode; if (region->range.base == regionTemp->range.base && region->range.size == regionTemp->range.size) { continue; } if (((region->range.base + region->range.size) > regionTemp->range.base) && (region->range.base < (regionTemp->range.base + regionTemp->range.size))) { VM_ERR("overlap between regions:\n" "flals:%#x base:%p size:%08x space:%p\n" "flags:%#x base:%p size:%08x space:%p", region->regionFlags, region->range.base, region->range.size, region->space, regionTemp->regionFlags, regionTemp->range.base, regionTemp->range.size, regionTemp->space); return -1; } RB_SCAN_SAFE_END(&space->regionRbTree, pstRbNode, pstRbNodeNext) return 0; } //shell task 进程虚拟内存的使用情况 UINT32 OsShellCmdProcessVmUsage(LosVmSpace *space) { LosVmMapRegion *region = NULL; LosRbNode *pstRbNode = NULL; LosRbNode *pstRbNodeNext = NULL; UINT32 used = 0; if (space == NULL) { return 0; } if (space == LOS_GetKVmSpace()) {//内核空间 OsShellCmdProcessPmUsage(space, NULL, &used); } else {//用户空间 RB_SCAN_SAFE(&space->regionRbTree, pstRbNode, pstRbNodeNext)//开始扫描红黑树 region = (LosVmMapRegion *)pstRbNode;//拿到线性区,注意LosVmMapRegion结构体的第一个变量就是pstRbNode,所以可直接(LosVmMapRegion *)转 used += region->range.size;//size叠加,算出总使用 RB_SCAN_SAFE_END(&space->regionRbTree, pstRbNode, pstRbNodeNext)//结束扫描红黑树 } return used; } //内核空间物理内存使用情况统计 VOID OsKProcessPmUsage(LosVmSpace *kSpace, UINT32 *actualPm) { UINT32 memUsed; UINT32 totalMem; UINT32 freeMem; UINT32 usedCount = 0; UINT32 totalCount = 0; LosVmSpace *space = NULL; LOS_DL_LIST *spaceList = NULL; UINT32 UProcessUsed = 0; UINT32 pmTmp; if (actualPm == NULL) { return; } memUsed = LOS_MemTotalUsedGet(m_aucSysMem1); totalMem = LOS_MemPoolSizeGet(m_aucSysMem1); freeMem = totalMem - memUsed; OsVmPhysUsedInfoGet(&usedCount, &totalCount); /* Kernel resident memory, include default heap memory */ memUsed = SYS_MEM_SIZE_DEFAULT - (totalCount << PAGE_SHIFT); spaceList = LOS_GetVmSpaceList();//获取虚拟空间链表,上面挂了所有虚拟空间 LOS_DL_LIST_FOR_EACH_ENTRY(space, spaceList, LosVmSpace, node) {//遍历链表 if (space == LOS_GetKVmSpace()) {//内核空间不统计 continue; } OsUProcessPmUsage(space, NULL, &pmTmp);//统计用户空间物理内存的使用情况 UProcessUsed += pmTmp;//用户空间物理内存叠加 } /* Kernel dynamic memory, include extended heap memory */ //内核动态内存,包括扩展堆内存 memUsed += ((usedCount << PAGE_SHIFT) - UProcessUsed); /* Remaining heap memory */ //剩余堆内存 memUsed -= freeMem; *actualPm = memUsed; } //shell task 物理内存的使用情况 VOID OsShellCmdProcessPmUsage(LosVmSpace *space, UINT32 *sharePm, UINT32 *actualPm) { if (space == NULL) { return; } if ((sharePm == NULL) && (actualPm == NULL)) { return; } if (space == LOS_GetKVmSpace()) {//内核空间 OsKProcessPmUsage(space, actualPm);//内核空间物理内存使用情况统计 } else {//用户空间 OsUProcessPmUsage(space, sharePm, actualPm);//用户空间物理内存使用情况统计 } } //虚拟空间物理内存的使用情况,参数同时带走共享物理内存 sharePm和actualPm 单位是字节 VOID OsUProcessPmUsage(LosVmSpace *space, UINT32 *sharePm, UINT32 *actualPm) { LosVmMapRegion *region = NULL; LosRbNode *pstRbNode = NULL; LosRbNode *pstRbNodeNext = NULL; LosVmPage *page = NULL; VADDR_T vaddr; size_t size; PADDR_T paddr; STATUS_T ret; INT32 shareRef; if (sharePm != NULL) { *sharePm = 0; } if (actualPm != NULL) { *actualPm = 0; } RB_SCAN_SAFE(&space->regionRbTree, pstRbNode, pstRbNodeNext) region = (LosVmMapRegion *)pstRbNode; vaddr = region->range.base; size = region->range.size; for (; size > 0; vaddr += PAGE_SIZE, size -= PAGE_SIZE) { ret = LOS_ArchMmuQuery(&space->archMmu, vaddr, &paddr, NULL); if (ret < 0) { continue; } page = LOS_VmPageGet(paddr); if (page == NULL) { continue; } shareRef = LOS_AtomicRead(&page->refCounts);//ref 大于1 说明page被其他空间也引用了,这就是共享内存核心定义! if (shareRef > 1) { if (sharePm != NULL) { *sharePm += PAGE_SIZE;//一页 4K 字节 } if (actualPm != NULL) { *actualPm += PAGE_SIZE / shareRef;//这个就有点意思了,碰到共享内存的情况,平分!哈哈。。。 } } else { if (actualPm != NULL) { *actualPm += PAGE_SIZE;//算自己的 } } } RB_SCAN_SAFE_END(&oldVmSpace->regionRbTree, pstRbNode, pstRbNodeNext) } //通过虚拟空间获取进程实体 LosProcessCB *OsGetPIDByAspace(LosVmSpace *space) { UINT32 pid; UINT32 intSave; LosProcessCB *processCB = NULL; SCHEDULER_LOCK(intSave); for (pid = 0; pid < g_processMaxNum; ++pid) {//循环进程池,进程池本质是个数组 processCB = g_processCBArray + pid; if (OsProcessIsUnused(processCB)) {//进程还没被分配使用 continue;//继续找呗 } if (processCB->vmSpace == space) {//找到了 SCHEDULER_UNLOCK(intSave); return processCB; } } SCHEDULER_UNLOCK(intSave); return NULL; } //统计虚拟空间中某个线性区的页数 UINT32 OsCountRegionPages(LosVmSpace *space, LosVmMapRegion *region, UINT32 *pssPages) { UINT32 regionPages = 0; PADDR_T paddr; VADDR_T vaddr; UINT32 ref; STATUS_T status; float pss = 0; LosVmPage *page = NULL; for (vaddr = region->range.base; vaddr < region->range.base + region->range.size; vaddr = vaddr + PAGE_SIZE) { status = LOS_ArchMmuQuery(&space->archMmu, vaddr, &paddr, NULL); if (status == LOS_OK) { regionPages++; if (pssPages == NULL) { continue; } page = LOS_VmPageGet(paddr); if (page != NULL) { ref = LOS_AtomicRead(&page->refCounts); pss += ((ref > 0) ? (1.0 / ref) : 1); } else { pss += 1; } } } if (pssPages != NULL) { *pssPages = (UINT32)(pss + 0.5); } return regionPages; } //统计虚拟空间的总页数 UINT32 OsCountAspacePages(LosVmSpace *space) { UINT32 spacePages = 0; LosVmMapRegion *region = NULL; LosRbNode *pstRbNode = NULL; LosRbNode *pstRbNodeNext = NULL; RB_SCAN_SAFE(&space->regionRbTree, pstRbNode, pstRbNodeNext) region = (LosVmMapRegion *)pstRbNode; spacePages += OsCountRegionPages(space, region, NULL); RB_SCAN_SAFE_END(&space->regionRbTree, pstRbNode, pstRbNodeNext) return spacePages; } CHAR *OsArchFlagsToStr(const UINT32 archFlags) { UINT32 index; UINT32 cacheFlags = archFlags & VM_MAP_REGION_FLAG_CACHE_MASK; UINT32 flagSize = FLAG_SIZE * BITMAP_BITS_PER_WORD * sizeof(CHAR); CHAR *archMmuFlagsStr = (CHAR *)LOS_MemAlloc(m_aucSysMem0, flagSize); if (archMmuFlagsStr == NULL) { return NULL; } (VOID)memset_s(archMmuFlagsStr, flagSize, 0, flagSize); switch (cacheFlags) { case 0UL: strcat_s(archMmuFlagsStr, flagSize, " CH\0"); break; case 1UL: strcat_s(archMmuFlagsStr, flagSize, " UC\0"); break; case 2UL: strcat_s(archMmuFlagsStr, flagSize, " UD\0"); break; case 3UL: strcat_s(archMmuFlagsStr, flagSize, " WC\0"); break; default: break; } static const CHAR FLAGS[BITMAP_BITS_PER_WORD][FLAG_SIZE] = { [0 ... (__builtin_ffsl(VM_MAP_REGION_FLAG_PERM_USER) - 2)] = "???\0", [__builtin_ffsl(VM_MAP_REGION_FLAG_PERM_USER) - 1] = " US\0", [__builtin_ffsl(VM_MAP_REGION_FLAG_PERM_READ) - 1] = " RD\0", [__builtin_ffsl(VM_MAP_REGION_FLAG_PERM_WRITE) - 1] = " WR\0", [__builtin_ffsl(VM_MAP_REGION_FLAG_PERM_EXECUTE) - 1] = " EX\0", [__builtin_ffsl(VM_MAP_REGION_FLAG_NS) - 1] = " NS\0", [__builtin_ffsl(VM_MAP_REGION_FLAG_INVALID) - 1] = " IN\0", [__builtin_ffsl(VM_MAP_REGION_FLAG_INVALID) ... (BITMAP_BITS_PER_WORD - 1)] = "???\0", }; for (index = FLAG_START; index < BITMAP_BITS_PER_WORD; index++) { if (FLAGS[index][0] == '?') { continue; } if (archFlags & (1UL << index)) { UINT32 status = strcat_s(archMmuFlagsStr, flagSize, FLAGS[index]); if (status != 0) { PRINTK("error\n"); } } } return archMmuFlagsStr; } VOID OsDumpRegion2(LosVmSpace *space, LosVmMapRegion *region) { UINT32 pssPages = 0; UINT32 regionPages; regionPages = OsCountRegionPages(space, region, &pssPages); CHAR *flagsStr = OsArchFlagsToStr(region->regionFlags); if (flagsStr == NULL) { return; } PRINTK("\t %#010x %-32.32s %#010x %#010x %-15.15s %4d %4d\n", region, OsGetRegionNameOrFilePath(region), region->range.base, region->range.size, flagsStr, regionPages, pssPages); (VOID)LOS_MemFree(m_aucSysMem0, flagsStr); } //dump 指定虚拟空间的信息 VOID OsDumpAspace(LosVmSpace *space) { LosVmMapRegion *region = NULL; LosRbNode *pstRbNode = NULL; LosRbNode *pstRbNodeNext = NULL; UINT32 spacePages; LosProcessCB *pcb = OsGetPIDByAspace(space);//通过虚拟空间找到进程实体 if (pcb == NULL) { return; } //进程ID | 进程虚拟内存控制块地址信息 | 虚拟内存起始地址 | 虚拟内存大小 | 已使用的物理页数量 spacePages = OsCountAspacePages(space);//获取空间的页数 PRINTK("\r\n PID aspace name base size pages \n"); PRINTK(" ---- ------ ---- ---- ----- ----\n"); PRINTK(" %-4d %#010x %-10.10s %#010x %#010x %d\n", pcb->processID, space, pcb->processName, space->base, space->size, spacePages); //虚拟区间控制块地址信息 | 虚拟区间类型 | 虚拟区间起始地址 | 虚拟区间大小 | 虚拟区间mmu映射属性 | 已使用的物理页数量(包括共享内存部分 | 已使用的物理页数量 PRINTK("\r\n\t region name base size mmu_flags pages pg/ref\n"); PRINTK("\t ------ ---- ---- ---- --------- ----- -----\n"); RB_SCAN_SAFE(&space->regionRbTree, pstRbNode, pstRbNodeNext)//按region 轮询统计 region = (LosVmMapRegion *)pstRbNode; if (region != NULL) { OsDumpRegion2(space, region); (VOID)OsRegionOverlapCheck(space, region); } else { PRINTK("region is NULL\n"); } RB_SCAN_SAFE_END(&space->regionRbTree, pstRbNode, pstRbNodeNext) return; } //查看所有进程使用虚拟内存的情况 VOID OsDumpAllAspace(VOID) { LosVmSpace *space = NULL; LOS_DL_LIST *aspaceList = LOS_GetVmSpaceList();//获取所有空间链表 LOS_DL_LIST_FOR_EACH_ENTRY(space, aspaceList, LosVmSpace, node) {//循环取出进程虚拟空间 (VOID)LOS_MuxAcquire(&space->regionMux); OsDumpAspace(space);//dump 空间 (VOID)LOS_MuxRelease(&space->regionMux); } return; } STATUS_T OsRegionOverlapCheck(LosVmSpace *space, LosVmMapRegion *region) { int ret; if (space == NULL || region == NULL) { return -1; } (VOID)LOS_MuxAcquire(&space->regionMux); ret = OsRegionOverlapCheckUnlock(space, region); (VOID)LOS_MuxRelease(&space->regionMux); return ret; } //dump 页表项 VOID OsDumpPte(VADDR_T vaddr) { UINT32 l1Index = vaddr >> MMU_DESCRIPTOR_L1_SMALL_SHIFT; LosVmSpace *space = LOS_SpaceGet(vaddr); UINT32 ttEntry; LosVmPage *page = NULL; PTE_T *l2Table = NULL; UINT32 l2Index; if (space == NULL) { return; } ttEntry = space->archMmu.virtTtb[l1Index];//找到 L1 页面项 if (ttEntry) { l2Table = LOS_PaddrToKVaddr(MMU_DESCRIPTOR_L1_PAGE_TABLE_ADDR(ttEntry));//找到L1页面项对应的 L2表 l2Index = (vaddr % MMU_DESCRIPTOR_L1_SMALL_SIZE) >> PAGE_SHIFT;//找到L2页面项 if (l2Table == NULL) { goto ERR; } page = LOS_VmPageGet(l2Table[l2Index] & ~(PAGE_SIZE - 1));//获取物理页框 if (page == NULL) { goto ERR; } PRINTK("vaddr %p, l1Index %d, ttEntry %p, l2Table %p, l2Index %d, pfn %p count %d\n", vaddr, l1Index, ttEntry, l2Table, l2Index, l2Table[l2Index], LOS_AtomicRead(&page->refCounts));//打印L1 L2 页表项 } else {//不在L1表 PRINTK("vaddr %p, l1Index %d, ttEntry %p\n", vaddr, l1Index, ttEntry); } return; ERR: PRINTK("%s, error vaddr: %#x, l2Table: %#x, l2Index: %#x\n", __FUNCTION__, vaddr, l2Table, l2Index); } //获取段剩余页框数 UINT32 OsVmPhySegPagesGet(LosVmPhysSeg *seg) { UINT32 intSave; UINT32 flindex; UINT32 segFreePages = 0; LOS_SpinLockSave(&seg->freeListLock, &intSave); for (flindex = 0; flindex < VM_LIST_ORDER_MAX; flindex++) {//遍历块组 segFreePages += ((1 << flindex) * seg->freeList[flindex].listCnt);//1 << flindex等于页数, * 节点数 得到组块的总页数. } LOS_SpinUnlockRestore(&seg->freeListLock, intSave); return segFreePages;//返回剩余未分配的总物理页框 } //dump 物理内存 /*********************************************************** * phys_seg:物理页控制块地址信息 * base:第一个物理页地址,即物理页内存起始地址 * size:物理页内存大小 * free_pages:空闲物理页数量 * active anon: pagecache中,活跃的匿名页数量 * inactive anon: pagecache中,不活跃的匿名页数量 * active file: pagecache中,活跃的文件页数量 * inactive file: pagecache中,不活跃的文件页数量 * pmm pages total:总的物理页数,used:已使用的物理页数,free:空闲的物理页数 ************************************************************/ VOID OsVmPhysDump(VOID) { LosVmPhysSeg *seg = NULL; UINT32 segFreePages; UINT32 totalFreePages = 0; UINT32 totalPages = 0; UINT32 segIndex; for (segIndex = 0; segIndex < g_vmPhysSegNum; segIndex++) {//循环取段 seg = &g_vmPhysSeg[segIndex]; if (seg->size > 0) { segFreePages = OsVmPhySegPagesGet(seg); #ifdef LOSCFG_SHELL_CMD_DEBUG PRINTK("\r\n phys_seg base size free_pages \n"); PRINTK(" -------- ------- ---------- --------- \n"); #endif PRINTK(" %08p %08p 0x%08x %8u \n", seg, seg->start, seg->size, segFreePages); totalFreePages += segFreePages; totalPages += (seg->size >> PAGE_SHIFT); PRINTK("active anon %d\n", seg->lruSize[VM_LRU_ACTIVE_ANON]); PRINTK("inactive anon %d\n", seg->lruSize[VM_LRU_INACTIVE_ANON]); PRINTK("active file %d\n", seg->lruSize[VM_LRU_ACTIVE_FILE]); PRINTK("inactice file %d\n", seg->lruSize[VM_LRU_INACTIVE_FILE]); } } PRINTK("\n\rpmm pages: total = %u, used = %u, free = %u\n", totalPages, (totalPages - totalFreePages), totalFreePages); } //获取物理内存的使用信息,两个参数接走数据 VOID OsVmPhysUsedInfoGet(UINT32 *usedCount, UINT32 *totalCount) { UINT32 index; UINT32 segFreePages; LosVmPhysSeg *physSeg = NULL; if (usedCount == NULL || totalCount == NULL) { return; } *usedCount = 0; *totalCount = 0; for (index = 0; index < g_vmPhysSegNum; index++) {//循环取段 physSeg = &g_vmPhysSeg[index]; if (physSeg->size > 0) { *totalCount += physSeg->size >> PAGE_SHIFT;//叠加段的总页数 segFreePages = OsVmPhySegPagesGet(physSeg);//获取段的剩余页数 *usedCount += (*totalCount - segFreePages);//叠加段的使用页数 } } } #ifdef __cplusplus #if __cplusplus } #endif /* __cplusplus */ #endif /* __cplusplus */