본문 바로가기

OS/vxworks

VxWorks 5.5에서 CPU 및 Memory 측정 방법


1. CPU 사용량 측정#

1.1 Profile Scope 사용#

     Profile Scope의 경우 Tornado의 RTI 툴의 하나로써 Tornado 구입시 선택할 수 있는 옵션 툴이다. 이 툴을 사용하면 사용자는 태스크 및 함수들의 CPU 사용량에 대해 매우 상세한 정보를 얻을 수 있다. 실행 화면은 다음과 같다.

profile_scope(1).jpg

      각 필드에서 확인할 수 있는 정보들은 다음과 같다.

  • Cumulative Sum % : Profile Scope가 정보를 수집하고 있는 동안의 누적된 CPU 사용율(서브루틴의 사용율을 포함한 값)
  • Current Sum % : 단기간 동안의 CPU 사용율의 값(서브루틴의 사용율을 포함한 값)
  • Current Direct % : 단기간 동안의 CPU 사용율의 값(서브루틴의 사용율은 포함하지 않음)
  • Maximum Sum % : 단기간 동안의 CPU 사용율 중 가장 높았던 CPU 사용율의 값(서브루틴의 사용율을 포함한 값)
  • Maximum Direct % : 단기간 동안의 CPU 사용율 중 가장 높았던 CPU 사용율의 값(서브루틴의 CPU사용율은 포함되지 않음)


  이 툴은 Task 및 함수별 CPU 사용율을 보여주는 GUI환경을 개발자에게 제공하고 있어 CPU 자원에 대한 상세한 정보를 확인할 수 있다. 하지만, Tornado 구입시 옵션으로 선택하는 툴이므로 추가 비용이 발생할 수도 있다.

 

      더 자세한 정보는 첨부한 ProfileManual.pdf 파일을 참고하시기 바랍니다.

1.2 spyLib 사용#

     Tornado에서 번들로 제공하고 있는 spy library를 사용하여 태스크와 인터럽트 핸들러 각각의 cpu의 사용량을 측정할 수 있다. 이 기능을 사용하기 위해서는 development tool components패키지에서 spy항목을 선택하여 컴파일해야 한다. spyLib는 다음의 api를 제공한다.

  • spyLibInit() : spyLib 패키지를 초기화한다.
  • spy() : 주기적으로 콘솔에 cpu 사용량을 출력하는 태스크를 실행시킨다.
  • spyReport() : cpu 사용량을 콘솔에 한번 출력한다.
  • spyClkStart() : cpu 사용량을 수집하는 작업을 시작한다.
  • spyClkStop() : cpu 사용량을 수집하는 작업을 중지한다.
  • spyStop() : cpu 사용량 수집하고 콘솔에 출력하는 작업을 중지한다. 내부적으로 spyClkStop을 호출한다.
  • spyHelp() : spyLib api들의 사용방법을 출력한다.

 

 vxworks-spy.jpg

 

 

     사용예제는 다음과 같다. 매초 200회의 비율로 정보를 수집하며, 10초마다 콘솔에 출력을 한다. ->

spy 10, 200

     delta 항목은 마지막에 보고된 이후부터 지금까지의 상황을 보여주며, total은 현재까지의 누적 상황을 보여준다.

NAME ENTRY TID PRI total % (ticks) delta % (ticks)

-------- -------- ----- --- --------------- --------------

tExcTask     _excTask    fbb58    0    0% (       0)    0% (       0)

tLogTask     _logTask    fa6e0    0    0% (       0)    0% (       0)

tShell       _shell      e28a8    1    0% (       4)    0% (       0)

tRlogind     _rlogind    f08dc    2    0% (       0)    0% (       0)

tRlogOutTask _rlogOutTa  e93e0    2    2% (     173)    2% (      46)

tRlogInTask  _rlogInTas  e7f10    2    0% (       0)    0% (       0)

tSpyTask     _spyTask    ffe9c    5    1% (     116)    1% (      28)

tNetTask     _netTask    f3e2c   50    0% (       4)    0% (       1)

tPortmapd    _portmapd   ef240  100    0% (       0)    0% (       0)

KERNEL                                 1% (     105)    0% (      10)

INTERRUPT                              0% (       0)    0% (       0)

IDLE                                  95% (    7990)   95% (    1998)

TOTAL                                 99% (    8337)   98% (    2083)



1.3 getCpuState 함수의 구현#

    cpu의 사용량을 측정하는 프로그램을 직접 구현할 수도 있다. 소스는 http://www.xs4all.nl/~borkhuis/vxworks/vxw_pt5.html#5.10-F 항목을 참고하였다.

     우선 시스템에서 돌아가는 사용자 태스크가 없다는 가정하에 우선 순위 255 짜리 태스크를 생성한다. 이 태스크는 innerIdle이란 전역변수를 계속적으로 증가시킨다. CPU가 1초간 동작하고 있을때 소비된 CPU시간을 innerIdle의 값으로 판단한다. 정확성을 위해 최초 세번에 걸쳐 측정된 innerIdle 값중 가장 큰 값을 maxIdleCount 변수에 저장한다.


static void CPUIdleTime(void)
{
    while (1)
    {
        while (innerIDLE <= UINT_MAX)
        {
            taskLock();
            innerIDLE++;
            taskUnlock();
        }
    }
}


     그 이후 CPU 사용량을 계산하는 우선 순위 1의 태스크를 생성한다. 이 태스크는 1초마다 깨어나, 현재 innderIdle의 값을 lastInnerIdle 변수에 저장하고 innerIdle 값을 0으로 초기화 한다. sysClkRateGet() 함수를 사용하여 정확히 1초간 sleep 할 수 있도록 한다. clearIdleCounters() 함수에서 위의 작업을 수행한다.

// function measureCPUIdleTime(void)
void measureCPUIdleTime(void)
{
    while (1)
    {
        clearIdleCounters();
        taskDelay(sysClkRateGet());
    }
}

// function clearIdleCounters(void)
//          resets the idle counters
//          to zero.
static void clearIdleCounters(void)
{
    STATUS status = semTake( CPUIdleSemId, sysClkRateGet() );

    lastInnerIDLE=innerIDLE;
    innerIDLE=0;

    status = semGive( CPUIdleSemId );
}


     CPU 사용량을 측정하기 원하는 시스템에 위의 두가지 태스크를 생성해주고, getCPUIdle()의 함수로 현재 CPU의 사용량의 값을 double 형태로 받을 수 있다. 계산하는 방식은 lastInnerIdle / maxIdleCount의 형태로 이루어진다.

// getCPUIdle returns CPU Idle Percent in double
//            e.g 50% returns 50.0
double getCPUIdle(void)
{
    double idlePercent=0;

    STATUS status = semTake( CPUIdleSemId, sysClkRateGet() );
    idlePercent = (double)( ((double)lastInnerIDLE / (double)maxIdleCount)*(double)100);
    status = semGive( CPUIdleSemId );

    if (idlePercent > 100.0)
    {
        maxIdleCount +=1;
        idlePercent=100.0;
    }

    printf("The current CPU IDLE is %5.1f%\n",idlePercent);
    return (idlePercent);
}

     실행 화면이다.

 

get_cpu_idle.jpg

 

2. Memory 사용량 측정#

2.1 MemScope의 사용#

     WindRiver Korea 기술문의 중 MemScope가 Heap의 상태만 보여주는 툴이라서 전체 시스템의 메모리 사용량 확인에는 적합하지 못한 툴이라는 답변을 들었다. 자세한 사항은 MemScopeManual.pdf 파일을 참고하시기 바랍니다.

 

2.2 memShow 커널 함수 사용#

     VxWorks 5.5 커널 API(memLib 부분)를 보면 memShow라는 함수를 제공한다. 이 함수의 목적은 현재 구동되고 있는 시스템의 메모리 사용량을 보여 주는 것에 있다. 인자로 1을 넘겨주면 Free Memory List의 정보까지 출력을 해준다.

example>

-> memShow(1)

FREE LIST:

  num     addr      size

  --- ---------- ----------

    0  0x22ea1e0      15296

    1  0x22ea178         88

    2  0x14ad198   14795896

SUMMARY:

 status    bytes     blocks   avg block  max block

 ------ ---------- --------- ---------- ----------

current

   free   14811280         3    4937093   14795896

  alloc     138680        60       2311          -

cumulative

  alloc     138768        61       2274          -

value = 0 = 0x0

->

     하지만 이 함수의 사용은 결과 값을 변수로 받을 수 없고, 단지 콘솔로만 출력이 가능하다. 물론 원하는 지점에서 memShow를 호출하여 메모리 사용량을 콘솔 화면으로 확인할 수도 있지만, 메모리 사용량을 판단하기 위해 그 결과를 항상 stdout으로 쓰는 것은 시스템에 무리를 줄 수 있을 뿐더러, 개발자가 메모리 사용량을 변수로 저장하여 다른 방법으로 가공할 수 있는 방법을 제공하지 않는다.

 

     ※ memShow의 결과값을 파이프로 연결하여 다른 곳으로 출력을 돌려 원하는 값을 추출할 수 있을 수도 있을 것 같다.

 

2.3 memPartInfoGet 커널 함수 사용#

     STATUS memPartInfoGet(PART_ID partId, MEM_PART_STATS *ppartStats) 의 프로토타입을 가지며 함수 호출시 ppartStats에 메모리 정보를 저장한다. 이 방법은 메모리의 상태를 콘솔로 출력하는 것이 아니라 MEM_PART_STATS 라는 자료 구조에 저장하여 개발자로 하여금 메모리 상태 정보를 원하는 방법으로 가공할 수 있는 방법을 제공한다.

memSysPartId는 시스템 전역 변수로서 시스템 메모리 파티션의 아이디를 가지고 있다.

void memCheck()

{

            MEM_PART_STATS sMemStats;

            if(memPartInfoGet(memSysPartId, &sMemStats) == ERROR)

            {

                        printf("\n memory Check Error !!!");

                        return;

            }

            printf("\n numBytesFree     [%d]", sMemStats.numBytesFree);

            printf("\n numBlocksFree    [%d]", sMemStats.numBlocksFree);

            printf("\n maxBlockSizeFree [%d]", sMemStats.maxBlockSizeFree);

            printf("\n numBytesAlloc    [%d]", sMemStats.numBytesAlloc);

            printf("\n numBlocksAlloc   [%d]", sMemStats.numBlocksAlloc);

}

 

 memPartInfoGet.jpg

 

 

2.4 getMemoryState 함수의 구현#

     VxWorks / Tornado II FAQ 사이트에 "How can I check if the memory heap is corrupted?" 의 항목을 보면 개발자가 VxWorks의 free memory list를 직접 접근하는 부분을 찾을 수 있다. 이 방법을 사용하여 현재 시스템의 메모리 상황을 확인하는 함수를 직접 작성할 수 있다.

     memPartInfoGet() 커널 API와 마찬가지로 getMemoryState의 함수에 MEM_STATE 타입의 자료형을 넘기면 현재 시스템의 메모리 현황을 볼 수 있다. 구현한 getMemoryState 함수를 사용하여 메모리의 현 상태를 받아 올 수 있으며, 메모리 현황을 감시하는 태스크를 만든다면 시스템의 메모리 현황을 시간대별로 확인할 수 있을 것이다. 또한 이 함수를 응용하면 원하는 값만 계산할 수 있으므로 오버로드를 줄일 수 있을 것이다.

mem_state.h

/**
 * VxWorks Memory State - mem_state.h
 * ---------------------------
 *
 *  created by Joohyun Lee(ppiazi@lignex1.com)
 */
#ifndef _MEM_STATE_H
#define _MEM_STATE_H
#include <private/mempartlibp.h>   //haha
#include <tasklib.h>
#define false 0
#define true 1
typedef struct
{
 int totalSpace;   // Total memory Space
 int totalAlloc;   // Total allocated memory space
 int totalFree;   // Total free memory space
 int totalAllocNodes; // The number of allocated memory nodes
 int totalFreeNodes;  // The number of free memory nodes
 int largestFree;  // The largest free memory node
} MEM_STATE;
void printMemoryState(MEM_STATE *a); // Print a memory state in fancy manner
int testMemoryState(void);    // Test module
int getMemoryState(MEM_STATE *a);  // Get memory infomation
#endif

mem_state.c

/**
 * VxWorks Memory State - mem_state.c
 * ---------------------------
 *
 *  created by Joohyun Lee(ppiazi@lignex1.com)
 *
 *  history
 *  -----------------------------------------------------------------
 *  version 0.2     2007.06.15        할당된 메모리 용량 및 노드 갯수
 *                                    에러처리 추가
 *                                    printMemoryState 함수 추가
 *  version 0.1     2007.06.14        미할당된 메모리 영역 계산
 */
#include <stdio.h>
#include <string.h>
#include "mem_state.h"
void printMemoryState(MEM_STATE *a)
{
// if ( a == NULL )
//  return;
 
 printf("Memory State Summary\n");
 printf(" status    bytes     blocks  avg block  max block\n");
    printf(" ------ ---------- --------- ---------  ----------\n");
    printf("  FREE  %10d       %4d       %10d       %10d\n",
      a->totalFree, a->totalFreeNodes, a->totalFree / a->totalFreeNodes, a->largestFree);
    printf("  ALLOC %10d       %4d       %10d       - \n",
      a->totalAlloc, a->totalAllocNodes, a->totalAlloc / a->totalAllocNodes);
}
int testMemoryState()
{
 MEM_STATE t;
 char *temp;
 /* 메모리를 할당하기 전에 현재 메모리 상태를 확인합니다.
  */
 printf(" --- step 1 ---\n");
 getMemoryState(&t);
 printMemoryState(&t);
 memShow(0);
 /* 16 bytes의 메모리를 할당하여 메모리 상태를 확인합니다.
  * 실제적으로는 24bytes가 할당되었다고 나옵니다. 왜 그럴까요?;;
  */
 temp = malloc(16);
 printf(" --- step 2 ---\n");
 getMemoryState(&t);
 printMemoryState(&t);
 memShow(0);
 
 /* 16 bytes 할당했던 메모리를 해제하여 다시 메모리 상태를 확인합니다.
  * memShow를 호출하여 동일한 값을 출력하는지 확인합니다.
  */
 free(temp);
 printf(" --- step 3 ---\n");
 getMemoryState(&t);
 printMemoryState(&t);
 memShow(0);
 return 0;
}
int getMemoryState(MEM_STATE *a)
{
 PARTITION *part;
 DL_NODE *ndFirst, *ndLast;
 DL_NODE *ndPrev = 0x00000000;
 DL_NODE *ndCurrent;
 // Use VxWorks default memory partition
 if ( memSysPartId == 0 )
  return -1;
 
 part = memSysPartId;
 
 // Take mem list semaphore
 semTake(&part->sem, WAIT_FOREVER);
 
 // Init variables to scan free memory list
 memset(a, 0, sizeof(MEM_STATE));  // set all variables of a to 0
 ndFirst = DLL_FIRST(&part->freeList); // get the first free memory node
 ndLast = DLL_LAST(&part->freeList);  // get the last free memory node
 ndCurrent = ndFirst;
 // start iteration
 while ( ndCurrent != 0x00000000 )
 {
  FREE_BLOCK  *pFreeBlock;
  unsigned int nBlockSize;
  int    bBlockUsed;
  pFreeBlock = (FREE_BLOCK *) NODE_TO_HDR(ndCurrent); // Pointer to free block header
  nBlockSize = pFreeBlock->hdr.nWords * 2;   // size in bytes
  if ( pFreeBlock->hdr.free )
   bBlockUsed = false;
  else
   bBlockUsed = true;
  // Validate node
  if ( pFreeBlock->node.previous != ndPrev )
  {
   printf("Node %p : Previous pointer %p invaild (should be %p)\n",
          pFreeBlock,
          pFreeBlock->node.previous,
          ndPrev);
   semGive(&part->sem);  // don't forget this line!!
   return -1;
  }
 
  if ( bBlockUsed )
  {
   printf("Node %p : Marked as used.\n", pFreeBlock);
   semGive(&part->sem);  // don't forget this line!!
   return -1;
  }
 
  // statistics
  a->totalFreeNodes ++;
  a->totalFree += nBlockSize;
  printf("Current Memory Free Block : %8d\n", nBlockSize);
 
  // find largest free memory block
  if ( nBlockSize > a->largestFree )
   a->largestFree = nBlockSize;
 
  // Goto next node in free list
  ndPrev = ndCurrent;
  ndCurrent = DLL_NEXT(&pFreeBlock->node);
 }
 a->totalAlloc = 2 * part->curWordsAllocated;
 a->totalAllocNodes = part->curBlocksAllocated;
 // Release mem list semaphore
 semGive(&part->sem);
 return 0;
}



 

이 글은 스프링노트에서 작성되었습니다.