저장공간 문제로 XCode 재 설치가 실패 할 때

XCode를 재설치 할 때 가능한 저장공간이 25GB이상 있음에도 저장공간이 부족해서 설치할 수 없다는 에러가 나온다면 제대로 삭제되지 않은 이전의 정보 때문일 수도 있다.

Uninstall xcode 10에 있는 내용을 참고해서 ~/Library/안에 있는 관련한 파일들을 삭제해주고 나니 저장공간에 대한 에러메세지 없이 잘 설치 되었다. /System/Library/안에 있는 파일들은 SIP 관련으로 그냥 삭제할 수 없고 복구모드로 부팅해서 csrutil을 실행해 주어야 한다는데 그냥 안해도 문제 없이 잘 되었다.

다음의 파일을 지우고 한번 시도해보자.

/Applications/Xcode.app 
~/Library/Caches/com.apple.dt.Xcode
~/Library/Developer
~/Library/MobileDevice
~/Library/Preferences/com.apple.dt.Xcode.plist
/Library/Preferences/com.apple.dt.Xcode.plist

Docker로 OpenGrok 설치

잘 쓰고 있던 OpenGrok 서버가 갑자기 맛이 가는 바람에 부랴부랴 대안을 찾아야 했는데 마땅한 서버가 없어서 로컬 머신에 Docker로 설치하는 방법을 찾아 보았다. 여기 소개된 내용은 Docker Hub에서 자세한 설명을 찾을 수 있다.

Docker가 설치되어 있다면 command창에서 다음의 명령으로 OpenGrok docker를 pull한다.

docker pull opengrok/docker

Pulling이 끝나면 목적에 소스와 indexing결과가 저장될 공간을 만들어 준다. src에는 분석할 소스를 넣고 bin에는 편의를 위한 스크립트를 넣을 예정이다.

mkdir -p ~/opengrok/bin
mkdir -p ~/opengrok/src
mkdir -p ~/opengrok/etc
mkdir -p ~/opengrok/data

이제, 8080 port에 접속 설정을 하고 위에서 만든 volume들을 docker에 마운트 시켜준다. Git server에 접근하기 위해 키 관련 설정을 해주어야 하는데, 귀찮아서 그냥 .ssh 디렉토리를 마운트 시켜 주었다.

docker run -d \
    --name opengrok \
    -p 8080:8080/tcp \
    -v ~/opengrok/bin/:/opengrok/bin/ \
    -v ~/opengrok/src/:/opengrok/src/ \
    -v ~/opengrok/etc/:/opengrok/etc/ \
    -v ~/opengrok/data/:/opengrok/data/ \
    -v ~/.ssh:/root/.ssh \
    opengrok/docker:latest

이제 해당 서버의 콘솔을 열고 인덱싱 명령을 수행하면 된다. GUI가 없다면 다음의 명령으로 실행 중인 docker에 접속할 수 있다.

docker exec -it <docker_container_id> bash

서버에 접속한 후 인덱싱을 수행하는 명령어는 다음과 같다.

export OPENGROK_DIR=/opengrok
java \
    -Djava.util.logging.config.file=$OPENGROK_DIR/etc/logging.properties \
    -Xmx1024m \
    -jar $OPENGROK_DIR/lib/opengrok.jar \
    -c /usr/local/bin/ctags \
    -s $OPENGROK_DIR/src -d $OPENGROK_DIR/data -H -P -S -G \
    -W $OPENGROK_DIR/etc/configuration.xml -U http://localhost:8080/

인덱싱이 끝나면 웹브라우져에서 http://localhost:8080으로 접속하면 된다.

위의 인덱싱 명령어가 너무 길어서 입력하기 힘들기 때문에 source code를 업데이트하고 인덱싱 하는 과정을 묶어서 다음과 같이 스크립트로 만들고 ~/opengrok/bin 안에 넣어 두면 편리하게 사용할 수 있다.

Linux에서 메모리 포인터의 유효성 검증

Windows에서와 달리 Linux환경에서는 딱히 포인터의 유효성을 검증할 수 있는 system call이 없다. 이 포스팅은 Linux환경에서 이와 유사한 기능을 구현하기 위해 “정보의 바다”에서 찾은 내용들을 정리해 둔 것이다.

1. _etext를 이용하는 방법

첫번째 방법은 Define and use a pointer validation function 이라는 위키문서에서 가져온 것인데 컴파일러가 생성하는 text 영역의 시작점을 이용해서 포인터 값이 이를 침범 하는지 여부를 검사하고 유효성을 판단한다. 하지만 위 링크의 커멘트에도 나와 있듯이 시스템에 따라 동작하지 않을 수도 있으므로 reliable 한 구현이라 볼 수는 없다.

bool isValidPointer_ET(void *ptr) {
    extern const char _etext;
    return (ptr != nullptr) && ((const char*)ptr > &_etext);
}

2. msync()를 이용하는 방법

Checking whether a pointer is valid in Linux라는 블로그 포스트에 소개된 방법으로 매핑된 메모리 공간을 동기화 할 때 쓰는 msync() 시스템 콜을 호출 하면서 유효하지 않은 page 시작 주소를 넘겨 주면 0이 아닌 음수 값을 반환하는 것을 이용하는 방법이다. (이 경우 errno에 ENOMEM이 설정된다)

#include <sys/mman.h>
#include <unistd.h>

bool isValidPointer_MS(void *ptr) {
    const size_t pageSize = sysconf(_SC_PAGESIZE);
    void *basePtr = (void *)((((size_t)ptr) / pageSize) * pageSize);
    return msync(basePtr, pageSize, MS_SYNC) == 0;
}

3. mincore()를 이용하는 방법

Stackoverflow에 올려진 Testing pointers for validity (C/C++)라는 질문에 대한 답변에 있는 아이디어 중 하나인데 메모리 페이지의 swap 상태를 확인해서 반환해 주는 mincore()을 이용하는 방법이다. 해당 답변에는 다른 아이디어 들도 있으니 필요에 따라 참고.

#include <sys/mman.h>
#include <unistd.h>

bool isValidPointer_MC(void *ptr) {
    unsigned char vec = 0;
    const size_t pageSize = sysconf(_SC_PAGESIZE);
    void *basePtr = (void *)((((size_t)ptr) / pageSize) * pageSize);
    int ret = mincore(basePtr, pageSize, &vec);
    return (ret == 0 && ((vec & 0x1) == 0x1));
}

시험 결과와 결론

위의 함수들에 대해 Linux환경에서 전역 변수 포인터, 지역 변수 포인터, 널 포인터, 널 포인터는 아니지만 명백하게 무효한 포인터(0x04 같은), 동적 할당된 공간에 대한 유효성 여부는 잘 동작한다.

하지만, 이미 해제된 포인터나 할당되지 않은 heap 공간 내의 임의 주소에 대해서는 제대로 유효성 여부를 판단하지 못하고, 주소 범위가 유효 하다면 포인터 역시 유효 하다고 판단하는 오류가 세가지 구현 모두에 있다.

    // 해제된 heap공간에 대한 유효성 여부 확인. 모두 실패함.
    unsigned int* dynamicVar = new unsigned int[100];
    delete[] dynamicVar;
    EXPECT_FALSE(isValidPointer_ET(dynamicVar));
    EXPECT_FALSE(isValidPointer_MS(dynamicVar));
    EXPECT_FALSE(isValidPointer_MC(dynamicVar));


    // 할당 되지 않은 Heap공간 내의 임의 포인터에 대한 유효성 확인. 모두 실패함.
    unsigned int* dynamicUnallocVar = dynamicVar + 100;
    EXPECT_FALSE(isValidPointer_ET(dynamicUnallocVar));
    EXPECT_FALSE(isValidPointer_MS(dynamicUnallocVar));
    EXPECT_FALSE(isValidPointer_MC(dynamicUnallocVar)); 

즉, 위의 구현 들은 주어진 포인터가 유효한 메모리 공간내에 속하는지는 확인할 수 있어도, 동적 할당 영역의 메모리 포인터가 실제 read/write 가능한 상태인지 여부는 정확히 반환 할 수 없다.

시험에 사용한 code는 여기에 붙여 둔다.