Pages

Friday, September 4, 2015

필요한 application의 현장 내 즉각적인 개발과 Qt

최근 현장에서 즉석으로 프로그램들을 만들 일이 있었습니다. 생산성 와방(......)인 Qt에 Qt Creator를 대동했습니다. 프로그램은 순식간에 만들어졌고, 원하는 결과를 정확하게 도출해냈습니다. 하지만 제게 그 경험은 일부 불편함을 남겼습니다.

Skeleton code를 제외하고 제가 작성한 핵심 코드는 약 10~20줄 남짓이었습니다. 그리고 전 20MB가 넘는 DLL들을 일일이 찾아 넣었고, 한데 모인 프로그램 패키지를 USB 메모리에 담아 각 PC에 꽂아가며 일일이 실행했습니다. PC에서 프로그램을 실행할 때마다 약 2~3초간의 지연이 있었고, 실행이 반복될수록 저 지연이 제게는 큰 부담으로 다가왔습니다.

Windows에서 Qt는 실행시 DirectX와 OpenGL을 기동시킵니다. 프로그램이 실제로 이 모듈들을 사용하는지의 여부는 상관없습니다. 제가 만든건 GDI 기반의 Qt Widgets 기반 프로그램이라 DirectX나 OpenGL이 끼어들 틈이 없었습니다만, Qt는 어쨌든 실행만 했다 하면 저 양대 그래픽 가속 시스템을 기동시킵니다. 그리고 저 모듈들은 최초 기동할때 최고 2~3초 정도를 잡아먹습니다. 이 모듈들이 이미 기동중이었으면 상관없지만, 사무용 PC 환경에서 저들 모듈이 기동하는건 팀장님 몰래 게임할 때(......) 빼고는 거의 없죠.

물론 splash screen을 띄워놓고 "기다리쇼"라고 할 정도의 큰 프로그램이라면 2~3초정도의 delay는 큰 문제가 되지 않습니다만, 현장에서 즉석에서 만들어져서 간단하게 쓸 프로그램에게 2~3초는 꽤 긴 시간입니다. 특히나 바쁜 상황일 경우, 성질 급한 사람들은 그 시간을 못 기다리고 엔터키를 연발하다가 instance를 수십개 넘게 만들어내는, 덕분에 프로그램의 실행이 더 지연되어 귀중한 시간을 낭비하는 상황까지 발생할 수 있습니다(뭐 사실 이런 사람들이 이해가 가기도 하는게, 가끔 보면 Windows가 키보드나 마우스 입력을 먹어버리는 경우가 있기도 하죠).

요즘 들어서 느끼는 거지만, Qt가 높은 생산성과 다양한 기능을 제공하는 이면에는 군데군데 발생하는 성능 이슈(꼭 cold start time이 아니더라도, linked list나 다른 여러 부분에서 미친듯이 속도가 느립니다)와 거대한 shared library가 있다는 생각이 듭니다. 일종의 tradeoff로 보고 있습니다만...... 물론 리눅스에서는 상황이 다릅니다만, Windows에서는 또 Windows만의 이야기가 있는 법이지요(GTK+ 계열 사용자시라면 또 다른 문제가 있을 것 같기도 합니다만, 그 부분은 다른 문제이니 여기서는 다루지 않겠습니다).

하여튼, 저런 상황을 겪고 난 이후 저는 또다시 Qt에서 wxWidgets로 옮겨왔습니다. Cold start시에도 instant로 번쩍하고 뜨고, 라이선스 정책 덕분에 static link에 대한 제약도 없어서 배포도 편합니다. 물론 구조적 특성상 생산성이 살짝 떨어진다거나 일부 기능이 없다거나 하는 문제도 있긴 하지만, 그 부분은 근성(......)과 직접 개발로 충분히 벌충할 수 있는 수준인 것 같습니다.

Qt는 정말 좋은 라이브러리입니다. 하지만 평양 감사도 저 싫으면 못하는 법이고, 모든 몸에 딱 맞는 하나의 치수를 가진 옷같은건 어디에도 존재하지 않지요. 당분간은 wxWidgets와 Code::Blocks로 연명하게 될 것 같습니다.

Sunday, June 14, 2015

분명 랜선이 잘 연결되어 있음에도 리눅스의 ip link나 ifconfig에서 "NO_CARRIER" 메시지가 뜬다면......

가끔가다 보면 사람 참 황당하게 될 때가 있죠. 랜선은 멀쩡하고, 인터페이스 카드도 불을 잘 깜빡이는데, 리눅스에서 ip linkifconfig를 쳐보면 "NO_CARRIER"메시지가 뜨는 경우가 있습니다. 더 황당한건, 이 하드웨어에서 Windows를 부팅해보면 인터넷이 멀쩡하게 잘 된다는 거죠.

얼마 전에 제가 경험한게 바로 이 증상입니다. 정확히는 이런 식이었습니다:
  1. Cold boot to Linux: NO_CARRIER
  2. Cold boot to Windows and reboot to Linux: 연결되네!(무시라?)
그러니까, 리눅스로 바로 부팅하면 네트웍이 안되고, Windows로 부팅했다가 리눅스로 부팅하면 리눅스에서 네트웍이 되는 황당한(......) 현상인 겝니다. 상식적으로 이해가 안되죠. Arch Linux로 Gentoo(stable)로 바꿔봤고, dhcpcd도 dhclient(dhcp ebuild에 있는거)로 바꿔봤습니다만 소용이 없더군요. 사실 Gentoo+dhclient 조합으로 되는줄 알았는데, 나중에 다시 해보니 증상이 그대로였습니다.
 구글링을 해보니 Arch Linux 포럼에 저와 비슷한 증상을 경험한 사람들이 꽤 있더군요. 그리고 atl1c 모듈 말고 다른 네트워크 카드에서도 증상이 일어났습니다(Marvel에서 온 사람들도 있고, Qualcomm Atheros의 다른 계열 카드도 있고......). 포럼의 집단지성이 뭔가 해결책을 찾긴 했지만, 제게는 통하지 않더군요. 그리고 제 문제는 딴 곳에 있었습니다.

해서, 하여간 이런 별난 경우가 발생했을때 어떻게 대처할지에 대해서, 제가 발견한 내용을 포함하여 두 가지 대응방안을 적어놓습니다:
  1. (직접 찾은 것)
    BIOS에서 "power up from PCI device" 기능을 끕니다. 전 그러니까 되더군요(......). 다른 "power up from....." 설정은 관련이 없는 것 같습니다(이를테면 키보드로 켜기, 마우스로 켜기 등). 저도 "power up from USB keyboard" 기능은 켜놓고 있습니다.
  2. (https://bbs.archlinux.org/viewtopic.php?pid=994513#p994513에서 확인)
    PCI 시스템에 "on" 메시지를 던집니다. 이를테면 이런 식입니다: echo on > /sys/bus/pci/devices/0000:00:XX.0/power/control
간단하죠?

이 황당한 증상으로 멘붕에 빠지신(......) 다른 분들께도 도움이 되시길 바랍니다.

What to do if ifconfig/ip link says "NO_CARRIER" in your Linux box even though the line is physically connected

There are times that make you into craziness. Your physical network connection is okay, the lamps on the interface card are flickering, and you see "NO_CARRIER" in your Linux box if you type ip link or ifconfig(deprecated). And guess what? In same machine, if you boot with Windows, you're actually connected to Internet!

Yes. That's what I experienced a few weeks ago. More precisely, the symptom was as follows:
  1. Cold boot to Linux: NO_CARRIER
  2. Cold boot to Windows and reboot to Linux: IT'S WORKING!(what?)
So, if I boot directly to Linux and I see NO_CARRIER message, but if I boot to Windows and reboot to Linux(warm boot) and my Linux can connect to the network as nothing happened.

Darn. Which curse did make my box as dull as possible like this? First I blamed my lovely bleeding-edge Arch Linux and built Gentoo stable, and later blamed dhcpcd and installed dhclient(in dhcp ebuild). I thought I got it with Gentoo+dhclient, but I was mistaken. Neither was the devil within.


I googled and in Arch Linux forum I found that there are several people with same issue, and it was NOT only atl1c module either. Some were using Marvel chipsets, some used the same Qualcomm Atheros, and so on. Their "collective intelligence" found out a solution, but it was of no effect to me(and some others in the forum), and I found yet the other cause for the issue.

So, here I leave things I found you can try(both googled ones and my own finding):
  1. (My own finding)
    Disable
    "power up from PCI device" feature from BIOS. Yes. From BIOS. And it's done. Other "power up from....." settings(e.g. keyboard, mouse) were actually not related to the issue in any way(still I enable "power up from USB keyboard" feature).
  2. (from https://bbs.archlinux.org/viewtopic.php?pid=994513#p994513)
    Send "on" message to the PCI system. e.g. echo on > /sys/bus/pci/devices/0000:00:XX.0/power/control

Simple, huh?

Hope this helps others with same symptom.

Tuesday, June 9, 2015

mingw-builds 5.1.0R0으로 Qt Creator 3.4.1을 빌드하기

mingw-builds 5.1.0 Release 0으로 Qt Creator 3.4.1을 빌드하는 동안 발견되는 문제들과 그 해결방안(이라기보단 우회방안)을 정리했습니다.

----------------------------------------
[QString::QString(const char *) is private]
문제가 되는 부분은 src/lib/corelib/tools/filetime_win.cpp의 108번째 줄입니다. QT_NO_CAST_FROM_ASCII이 선언되었을때 QString의 헤더 파일과 관련되어 있는 것 같긴 한데, 자세한건 잘 모르겠네요.

일단 보시면, 해당 부분은 이렇게 생겼습니다:
const QString result = QString("%1.%2.%3 %4:%5:%6")

이 부분을 이렇게 바꾸면 어쨌든 해결됩니다:
const QString result = QString(QLatin1String("%1.%2.%3 %4:%5:%6"))

뭐 일단 컴파일이 되는게 중요하죠. ;)

----------------------------------------
[Psapi.lib is not found]
이 건 역시 qbs 빌드시 발생하는 오류입니다. 경우에 따라 src/shared/qbs/src/lib/corelib디렉토리에 있는 Makefile.Debug 또는 Makefile.Release 파일을 수정해야 합니다. 텍스트 에디터로 파일을 연 뒤 Psapi.lib를 찾아보면 다음과 같은 내용을 발견할 수 있습니다:

LIBS        =        -lglu32 -lopengl32 -lgdi32 -luser32 Psapi.lib -LD:/Components/Qt-5.4.2-MinGW32-5.1.0R0/qtbase/lib -lQt5Script -lQt5Gui -lQt5Xml -lQt5Core release\qbscore_resource_res.o 

아래와 같이 Psapi.lib-lpsapi로 바꿔주기만 하면 문제가 해결됩니다:
LIBS        =        -lglu32 -lopengl32 -lgdi32 -luser32 -lpsapi -LD:/Components/Qt-5.4.2-MinGW32-5.1.0R0/qtbase/lib -lQt5Script -lQt5Gui -lQt5Xml -lQt5Core release\qbscore_resource_res.o

아무래도 VC nmake에서 GNU makefile로 설정을 갖다붙이다가 실수한 것 같습니다. 어째 개발진 양반들이 파일 만들면서 딴생각을 한 것 같네요. :P

----------------------------------------
[MIB_TCP_STATE is not declared]
만일 Visual Studio를 쓴다면 단지 iphlpapi.h를 인클루드하는 것으로 모든게 해결됩니다만, MinGW에서는 이걸 손수 선언해줘야 합니다. src/lib/utils에 있는 tcpportsgatherer.cpp파일을 보시면 이런 내용을 보실 수 있습니다:
// Missing declarations for MinGW 32.
#if __GNUC__ == 4 && (!defined(__MINGW64_VERSION_MAJOR) || __MINGW64_VERSION_MAJOR < 2)
typedef enum { } MIB_TCP_STATE;
#endif

아무래도 GCC 버전이 4.X일때만 동작하는 것 같죠? #if 부분을 comment out하는 걸로 해결할 수 있습니다.
// Missing declarations for MinGW 32. 
//#if __GNUC__ == 4 && (!defined(__MINGW64_VERSION_MAJOR) || __MINGW64_VERSION_MAJOR < 2)
typedef enum { } MIB_TCP_STATE;
//#endif

이걸로 끝입니다. Qt Creator 3.4.1로 오신 여러분을 환영합니다!

Building Qt Creator 3.4.1 with mingw-builds 5.1.0R0

There are some issues in building Qt Creator with mingw-builds 5.1.0 Release 0. Here are compile errors and workarounds:

----------------------------------------
[QString::QString(const char *) is private]

The issue occurs in building qbs and on line 108 of file src/lib/corelib/tools/filetime_win.cpp. It seems to be related to QString header file when QT_NO_CAST_FROM_ASCII is defined, but I have no idea on details.

You can see the line looks like this:
const QString result = QString("%1.%2.%3 %4:%5:%6")


And you can change the line like this to workaround:
const QString result = QString(QLatin1String("%1.%2.%3 %4:%5:%6"))

Now it's resolved anyway. ;)

----------------------------------------
[Psapi.lib is not found]
One more build error when building qbs. You've got to manually edit either Makefile.Debug or Makefile.Release in src/shared/qbs/src/lib/corelib directory. Search for Psapi.lib with your favorite text editor, and you'll see this:
LIBS        =        -lglu32 -lopengl32 -lgdi32 -luser32 Psapi.lib -LD:/Components/Qt-5.4.2-MinGW32-5.1.0R0/qtbase/lib -lQt5Script -lQt5Gui -lQt5Xml -lQt5Core release\qbscore_resource_res.o 

Just change Psapi.lib to -lpsapi as shown below and it's done:
LIBS        =        -lglu32 -lopengl32 -lgdi32 -luser32 -lpsapi -LD:/Components/Qt-5.4.2-MinGW32-5.1.0R0/qtbase/lib -lQt5Script -lQt5Gui -lQt5Xml -lQt5Core release\qbscore_resource_res.o

It seems to be human error when copy-and-pasting VC nmake file to GNU makefile. Hey Trolls, WAKE UP!

----------------------------------------
[MIB_TCP_STATE is not declared]
If you're using Visual Studio, it's okay by just including iphlpapi.h in your source code, yet with MinGW you have to declare yourself. If you read the file tcpportsgatherer.cpp in src/lib/utils you'll see something like this:
// Missing declarations for MinGW 32.
#if __GNUC__ == 4 && (!defined(__MINGW64_VERSION_MAJOR) || __MINGW64_VERSION_MAJOR < 2)
typedef enum { } MIB_TCP_STATE;
#endif

Well, this seems to work only if you're using GCC version 4.X, not with 5.1.0. Just comment #if like this and you're done.
// Missing declarations for MinGW 32.
//#if __GNUC__ == 4 && (!defined(__MINGW64_VERSION_MAJOR) || __MINGW64_VERSION_MAJOR < 2)
typedef enum { } MIB_TCP_STATE;
//#endif

Now everything's done. Welcome to the world of Qt Creator 3.4.1! :)

Saturday, May 2, 2015

Windows에서 Qt Widget 프로그램을 cold start하면 시간이 얼마나 오래 걸릴까?

아마 다들 아시겠지만, Qt는 시스템 위젯을 사용하는 대신 전용 위젯을 따로 사용합니다. 덕분에 DLL들 크기가 꽤 큰 편입니다(Qt Core와 Qt GUI, 그리고 기본 플러그인 몇 개만 합쳐도 20MB를 넘어습니다). 뒤집어서 말하자면 프로그램이 cold start를 하면 디스크 읽기 때문에 상대적으로 시작이 느릴 수밖에 없습니다.
그런데, 느리다면 얼마나 느리려나요? 스톱워치로 재보니 한 4초쯤 되는 것 같긴 하던데, 프로그램 동작을 사람 눈대중으로 측정하는건 프로그래머가 할 짓은 아닌 것 같더군요. 해서 Qt Creator가 기본으로 제공하는 Qt Widget application 소스코드를 살짝 수정해봤습니다:

#include “widget.h”
#include <QApplication>
#include <QDateTime>
#include <QMessageBox>
int main(int argc, char *argv[])
{
  QDateTime start=QDateTime::currentDateTime();
  QApplication a(argc, argv);
  Widget w;
  w.show();
  QMessageBox::information(NULL, “Time”, QString::number(start.secsTo(QDateTime::currentDateTime())));
  return 0;
}

간단하죠?
이 프로그램을 2012년쯤에 산 펜티엄에서 돌려보니 한 2초 정도가 걸리더군요. Warm start(재시작)시에 걸린 시간은 1초 미만이라 별 의미는 없어 보입니다.
그런데 이런 쓸데없는 결과에 관심있으신 분이 있으시긴 하려나(……).

How slow is it to cold start a Qt Widget application on Windows?

As you know, Qt uses its own widgets rather than using one system provides. So its DLLs are large(at least 20MB or so for just Qt Core + Qt GUI + some default plugins) so that it takes a bit relatively long to cold start the application.

But how long? I took my stopwatch application on my phone and it says it’s about 4 seconds for just showing blank window, but it’s not fair to rely on human sensory as a programmer myself.

So I tweaked the default Qt Widget application provided by Qt Creator as follows:


#include “widget.h”
#include <QApplication>
#include <QDateTime>
#include <QMessageBox>

int main(int argc, char *argv[])
{
   QDateTime start=QDateTime::currentDateTime();
   QApplication a(argc, argv);
   Widget w;
   w.show();

   QMessageBox::information(NULL, “Time”, QString::number(start.secsTo(QDateTime::currentDateTime())));
   return 0;
}


SImple, huh?

I ran the application on my Pentium which I bought at 2012 or so and found out that it takes 2 seconds to create the widget and show. For warm start(restart), it takes less than 1 second so it’s meaningless.

Anyone interested in this result?