2014-04-01

My experience to trouble shoot a notebook computer powering off suddenly


I like programming (hence debugging) but I am not particular interested in trouble-shooting PC installation issues.

A friend of my wife has recently got a second hand notebook PC for his son.  However, the machine will power off randomly, sometimes as early as 1 minute right after power on.  However, if the PC is is booted in Windows Safe mode, the symptom will disappear.

The PC configuration is as follows:

Model: LG R405-A
Operating System: Windows 7 Enterprise
Video card: ATI Radeon Xpress 1250

Why I highlight the Video card because it was also reported there will be AppCrash (about atiundag,dll) when the Powerpoint slideshow mode is activated (and also within Desktop Window Manager (DWM) when the Windows starts).

Therefore my first impression is about the ATI driver version.  However, Windows Update shows it is already running the latest version.

Unbelieving this fact, I go to the ATI web site (now AMD) to download some new version of drivers.  After rebooting, the AppCrash still appears.  Worst still, the random power off problem also still occurs.

However, I notice that right before the powering off, I can hear a sudden increase of the ventilation fan noise.  In fact, from some internet forums, some people has already said that this symptom may be due to overheating.  Then I install some temperature monitoring software and I find that the CPU temperature rises gradually to nearly 100°C and then the machine will shut down.  Then I realize that the previous so called randomness is just due to my testing pattern.  If the machine is off whole night and then power up, then it can sustain longer until the temperature reaches the threshold.  But if I repeatedly reboot the machine and do not wait for it to cool down, of course the temperature problem is not solved and it can reach the threshold within 1 minute after booting up.

At this stage, I still think the ATI driver is the root cause of temperature.  Then I try to go to the LG web site to try to download the ATI driver.  Because Xpress 1250 is a really old chip, in fact, there is no Windows 7 driver for it.  In the LG web site, the latest version is only for Vista.  I did download to try anyway.

To my surprise, the AppCrash problem is solved immediately with the Vista driver.

However, before I can feel happy, the machine turns off again suddenly!

Then I start to think whether there is any hardware issue (e.g. blocked ventilation or bad cooling agent) that causes the high temperature.  I do not want to dismantle the notebook PC.  So, I resort to use the Windows Power Plan to limit the maximum CPU utilization to 50%.  Okay, then the temperature is only around 85°C and does not crash again even though I run YouTube to play video repeatedly.  It seems that the problem is solved (although not fundamentally but at least superficially).

I try to check if there is any fan control utility that can force the fan to always on (I have already setup the BIOS setting but the fan speed is not high).  When I use the SpeedFan software, I notice suddenly that both cores of the CPU is running at 100%.

When I check the task manager, I find that a process dgen.exe is eating my CPU.  It is a virus.  How come I never think about this?  (At a second thought, although the CPU is at 100%, it is not sluggish and still very responsive.)  Afterwards, I update the anti-virus engine and eradicate any other remaining viruses found in the computer.

Postscript: Although the root cause is traced now, I still cannot understand how come the LG computer is so designed that if the CPU keeps on running at 100% loading, the cooling mechanism is not sustainable to keep the temperature within the working limit.

2014-03-04

懷念舅公關傑才先生


在剛過去的農曆新年,和母親提起小時候會去澳門同外祖父母拜年。而且澳門還有一位舅公,他在澳門有一所英語學校,但我年紀小,沒有聽過他說英文,不過他曾送我一本他用中文寫的英文工具書,書名叫《英文解字》,這書的包裝很特別,是一本線裝書!



忽然間有點懷念這一位舅公,但對他的認識不深,只好靠互聯網找找有關他的資料。原本他也編寫過幾本書,其中一本《英譯廣東口語詞典》,在台灣博客來上的作者簡介如下:


關傑才


資深教育工作者,翻譯工作者。先後擔任過中學教務主任;中學高年級英文教師;公開大學特約講師及文字翻譯等職。有豐富的教中國人學英語和教外國人學廣東話的教學經驗。曾編譯出版《英文解字》一書,本書為其又一力作。

而另一編
趙汝能寫給關傑才《詞語疑音字正讀》的序
則見於: http://bbs.qoos.com/viewthread.php?tid=1544660


我在書櫃再找出他送我的《英文解字》線裝書,由於在網上再找不到此書的資料,故我決定將此孤本數碼化,以作紀念!

http://waihungmm.web.fc2.com/english/

另外,下載PDF可於https://drive.google.com/file/d/0Bzo1pJKfp9QELWFKTjlSNUUtZWM/view?usp=sharing



2014-01-22

Detecting errors in jpeg file using libjpeg-turbo

Recently I have the need to detect the integrity of a jpeg file. First I try to see the famous libjpeg (wiki) library can do the job. I am using Visual Studio (i.e. windows) to write my codes and I download the win32 version of libjpeg from internet. However, I keep on failure because the win32 libjpeg library keeps on emitting "Access Violation" at runtime. I try to solve the problem by googling solution but in vain.

Later I turn to use libjpeg-turbo although I really do not need to performance of SIMD instructions. Luckily libjpeg-turbo has a Visual Studio version of static library and I do to encounter the runtime error in libjpeg any more.

Basically my codes should be simply, as illustrated by the following pseudo-codes:

jpeg_create_decompress();
fopen(jpeg_file);
jpeg_stdio_src();
jpeg_read_header();
jpeg_start_decompress();
while loop {
  jpeg_read_scanlines();
  }
jpeg_finish_decompress();
jpeg_destroy_decompress();
fclose();

However, to my surprise, libjpeg use a setjmp/longjmp approach to detect and handle the errors. Frankly speaking, although I have been using C languages for years, I really do not like the GOTO style of handling errors. The libjpeg is emulating an Object-oriented approach by allowing programmer to build an error hander to intercept the errors.

I am interested in two kinds of errors:
  • Fatal errors: the library will quit in this case
  • Warnings: the library can continue with data corruption
The former method is called "error_exit()" and hte latter will invoke "output_message()". So, my job is to build my version of error_exit() and output_message(), with pseudo codes as follows:

my_error_exit ()
{

  /* store the error code in the global variable */
  /* store the error message in the global variable */
  longjmp (myerr->setjmp_buffer, 1); /* Return control to the setjmp point */
}

my_output_message ()
{
  /* store the error code in the global variable */
  /* store the error message in the global variable */
}

The full source listing is as follows:

#include 
#include "stdafx.h"
#include "jpeglib.h"
#include 
#include 

int error_message_code;
char error_message_buffer[JMSG_LENGTH_MAX];

struct my_error_mgr {
  struct jpeg_error_mgr pub;
  jmp_buf setjmp_buffer;
};

typedef struct my_error_mgr * my_error_ptr;

METHODDEF(void)
my_error_exit (j_common_ptr cinfo)
{
  my_error_ptr myerr = (my_error_ptr) cinfo->err;
  error_message_code = cinfo->err->msg_code; /* store the code in the global variable */
  (*cinfo->err->format_message) (cinfo, error_message_buffer);  /* store the message in the global variable */
  printf ("Error intercepted in my_error_exit(): error_code = %d message = \'%s\'\n",
   error_message_code, error_message_buffer);
  longjmp(myerr->setjmp_buffer, 1); /* Return control to the setjmp point */
}

METHODDEF(void)
my_output_message (j_common_ptr cinfo)
{
  error_message_code = cinfo->err->msg_code;
  (*cinfo->err->format_message) (cinfo, error_message_buffer);
}

int _tmain(int argc, _TCHAR* argv[])
{
  struct jpeg_decompress_struct cinfo;
  struct my_error_mgr jerr;
  FILE * infile;
  JSAMPARRAY ptr;  /* Output row buffer */
  int row_stride;  /* physical row width in output buffer */

  cinfo.err = jpeg_std_error(&jerr.pub);
  jerr.pub.error_exit = my_error_exit;
  jerr.pub.output_message = my_output_message;
  /* Establish the setjmp return context for my_error_exit to use. */
  if (setjmp(jerr.setjmp_buffer)) {
    jpeg_destroy_decompress(&cinfo);
    fclose(infile);
    return 0;
  }

  jpeg_create_decompress(&cinfo);

  if ((infile = fopen("C:\\bin\\libjpeg-turbo\\b.jpg", "rb")) == NULL) {
 printf("cannot open jpeg file");
 return 1;
 }

  jpeg_stdio_src(&cinfo, infile);

  (void) jpeg_read_header(&cinfo, TRUE);

  jpeg_start_decompress(&cinfo);

  row_stride = cinfo.output_width * cinfo.output_components;
  ptr = (*cinfo.mem->alloc_sarray)((j_common_ptr)&cinfo,JPOOL_IMAGE,row_stride,1);
 
  while (cinfo.output_scanline < cinfo.output_height) {
    error_message_code = 0;
    jpeg_read_scanlines(&cinfo, (JSAMPARRAY) ptr, 1);
    if (error_message_code) 
       printf("Error occured at scanline %d: Error code = %d message= \'%s\'\n",
         cinfo.output_scanline, error_message_code, error_message_buffer);
    }  // while

  (void) jpeg_finish_decompress(&cinfo);

  jpeg_destroy_decompress(&cinfo);

  fclose (infile);

  return 0;
}

Postscript

I prepare two corrupted jpeg files. The first is truncated file that the exact image height is smaller than that is specified in the jpeg header. In this case, I can successfully intercept the error at scanline 313 with Error code = 120 and error message = "Premature end of JPEG file". The second is truncated that even the jpeg header cannot be successfully read. In this case, my my_error_exit() terminating route can capture the error_code = 51 and error message = "JPEG datastream contains no image".

2013-10-28

南蓮園池之微距攝影

每次都有不同感覺,今次玩Macro


















2013-10-03

雪糕甜品

如果不是老婆有coupon,我也不捨得花錢買咁貴的雪糕吃!


2013年溫暖人間晚宴

幸得朋友包了一圍,令我有機會出席這盛大又溫馨的晚宴。




2013-10-01

肥陶人@油塘

近來連續幾個週末都去了油塘大本型商場試新食市,要小心身體會變成像港鐵站外的肥陶人!


2013-08-27

Creating a Full Blog list Gadget

I try to create a full blog list.  Originally I thought Google should have a built-in gadget for this purpose but I was wrong.   So finally after reviewing other blogger's proposal, I implement my way as follows:



(1) From the Layout menu, choose "Add a Gadget"







(2) Choose the "HTML/Javascript" Gadget







(3) Input the Title and enter the HTML in the Content box







-----
<div id="bp_toc"></div>
<script>
// ---------------------------------------------------
// BLOGTOC
// ---------------------------------------------------
// BlogToc creates a clickable Table Of Contents for
// Blogger Blogs.
// It uses the JSON post feed, and create a ToC of it.
// The ToC can be sorted by title or by date, both
// ascending and descending, and can be filtered by
// label.
// ---------------------------------------------------
// Author: Beautiful Beta
// Url: http://beautifulbeta.blogspot.com
// Version: 2
// Date: 2007-04-12
// ---------------------------------------------------
// Modified by Aneesh
// www.bloggerplugins.org
// Date : 02-08-2011
// ---------------------------------------------------
// Modified by waihungmm
// Date : 2013-08-21

   var postTitle = new Array();     // array of posttitles
   var postUrl = new Array();       // array of posturls
   var postDate = new Array();      // array of post publish dates
   var postSum = new Array();       // array of post summaries
   var postLabels = new Array();    // array of post labels

// global variables
   var sortBy = "titleasc";         // default value for sorting ToC
   var tocLoaded = false;           // true if feed is read and ToC can be displayed
   var numChars = 250;              // number of characters in post summary
   var postFilter = '';             // default filter value
   var tocdiv = document.getElementById("bp_toc"); //the toc container
   var totalEntires =0; //Entries grabbed till now
   var totalPosts =0; //Total number of posts in the blog.

// main callback function

function loadtoc(json) {

   function getPostData() {
   // this functions reads all postdata from the json-feed and stores it in arrays
      if ("entry" in json.feed) {
         var numEntries = json.feed.entry.length;
         totalEntires = totalEntires + numEntries;
         totalPosts=json.feed.openSearch$totalResults.$t

      // main loop gets all the entries from the feed
         for (var i = 0; i < numEntries; i++) {
         // get the entry from the feed
            var entry = json.feed.entry[i];

         // get the posttitle from the entry
            var posttitle = entry.title.$t;

         // get the post date from the entry
            var postdate = entry.published.$t.substring(0,10);

         // get the post url from the entry
            var posturl;
            for (var k = 0; k < entry.link.length; k++) {
               if (entry.link[k].rel == 'alternate') {
               posturl = entry.link[k].href;
               break;
               }
            }

         // get the post contents from the entry
         // strip all html-characters, and reduce it to a summary
            if ("content" in entry) {
               var postcontent = entry.content.$t;}
            else
               if ("summary" in entry) {
                  var postcontent = entry.summary.$t;}
               else var postcontent = "";
         // strip off all html-tags
            var re = /<\S[^>]*>/g;
            postcontent = postcontent.replace(re, "");
         // reduce postcontent to numchar characters, and then cut it off at the last whole word
            if (postcontent.length > numChars) {
               postcontent = postcontent.substring(0,numChars);
               var quoteEnd = postcontent.lastIndexOf(" ");
               postcontent = postcontent.substring(0,quoteEnd) + '...';
            }

         // get the post labels from the entry
            var pll = '';
            if ("category" in entry) {
               for (var k = 0; k < entry.category.length; k++) {
                  pll += '<a href="javascript:filterPosts(\'' + entry.category[k].term + '\');" title="Click here to select all posts with label \'' + entry.category[k].term + '\'">' + entry.category[k].term + '</a>,  ';
               }
            var l = pll.lastIndexOf(',');
            if (l != -1) { pll = pll.substring(0,l); }
            }

         // add the post data to the arrays
            postTitle.push(posttitle);
            postDate.push(postdate);
            postUrl.push(posturl);
            postSum.push(postcontent);
            postLabels.push(pll);
         }
      }
      if(totalEntires==totalPosts) {tocLoaded=true;showToc();}
   } // end of getPostData

// start of showtoc function body
// get the number of entries that are in the feed
//   numEntries = json.feed.entry.length;

// get the postdata from the feed
   getPostData();

// sort the arrays
   sortPosts(sortBy);
   tocLoaded = true;
showToc(); // newly added
}

// filter and sort functions


function filterPosts(filter) {
// This function changes the filter
// and displays the filtered list of posts
  // document.getElementById("bp_toc").scrollTop = document.getElementById("bp_toc").offsetTop;;
   postFilter = filter;
   displayToc(postFilter);
} // end filterPosts

function allPosts() {
// This function resets the filter
// and displays all posts

   postFilter = '';
   displayToc(postFilter);
} // end allPosts

function sortPosts(sortBy) {
// This function is a simple bubble-sort routine
// that sorts the posts

   function swapPosts(x,y) {
   // Swaps 2 ToC-entries by swapping all array-elements
      var temp = postTitle[x];
      postTitle[x] = postTitle[y];
      postTitle[y] = temp;
      var temp = postDate[x];
      postDate[x] = postDate[y];
      postDate[y] = temp;
      var temp = postUrl[x];
      postUrl[x] = postUrl[y];
      postUrl[y] = temp;
      var temp = postSum[x];
      postSum[x] = postSum[y];
      postSum[y] = temp;
      var temp = postLabels[x];
      postLabels[x] = postLabels[y];
      postLabels[y] = temp;
   } // end swapPosts

   for (var i=0; i < postTitle.length-1; i++) {
      for (var j=i+1; j<postTitle.length; j++) {
         if (sortBy == "titleasc") { if (postTitle[i] > postTitle[j]) { swapPosts(i,j); } }
         if (sortBy == "titledesc") { if (postTitle[i] < postTitle[j]) { swapPosts(i,j); } }
         if (sortBy == "dateoldest") { if (postDate[i] > postDate[j]) { swapPosts(i,j); } }
         if (sortBy == "datenewest") { if (postDate[i] < postDate[j]) { swapPosts(i,j); } }
      }
   }
} // end sortPosts

// displaying the toc

function displayToc(filter) {
// this function creates a three-column table and adds it to the screen
   var numDisplayed = 0;
   var tocTable = '';
   tocTable += '<table>';

   tocTable += '</tr>';
   for (var i = 0; i < postTitle.length; i++) {
         tocTable += '<tr><td class="toc-entry-col1"><a href="' + postUrl[i] + '" target=_blank>' + postTitle[i] + '</a></td></tr>';
         numDisplayed++;
   }
   tocTable += '</table>';

tocdiv.innerHTML = tocTable;
} // end of displayToc

function showToc() {
  if (tocLoaded) {
     displayToc(postFilter);
     var toclink = document.getElementById("toclink");
 
  }
  else { alert("Just wait... TOC is loading"); }
}

</script>
<script src="http://your_blog_name.blogspot.com/feeds/posts/default?alt=json-in-script&max-results=999&callback=loadtoc"></script>
-----



(4) The result will be as follows





(5) This is the final output:

2013-04-03

2012-11-19

知魚之痛 (Metropop Issue 331 @ 2012-11-15)


惠子曾問「子非魚,安知魚之樂?」確實,對比起會跑會叫的陸上動物,我們一直不太相信魚類是有感受的動物,以至對牠們的同情心亦都相對較少:各國的動物權益條例一直把魚類豁免在外,釣魚仍被視為一種健康的休閒活動……但近十年愈來愈多科學家證實,魚其實與貓、狗、人一樣也有靈性時,我們仍可問心無愧地把快樂建築在牠們的痛苦之上嗎?

知魚知痛
魚的神經結構與人類及其他哺乳類動物相似,全身上下包括經常被魚?勾傷的唇部,都佈滿了能感知痛楚的神經末梢。更有研究指魚跟人類一樣,有一種具有鎮痛作用的腦分泌「安多芬」(endorphins),如果沒有痛覺,又哪來鎮痛?

放生仁慈嗎?
有些垂釣愛好者與「戰利品」拍照留念後,選擇把牠們放生回大海,這樣做比較仁慈嗎?首先,這過程已令魚類承受到很大的痛楚,如果目的純粹為自己高興,這種娛樂是否人道?每天無數的魚兒被纏困於魚網之中,或被金屬利?刺穿身體,再拖到水面無法呼吸,牠們的魚鰓經常因此而塌陷,魚鰾也會因突然驟變的壓力而破裂,又或失去保護身體的魚鱗而死去。美國奧克拉荷馬州野生動物保護局便發現,近43%被釣上後再放生的魚,會在六天內死去。

魚智慧
除能感受痛楚,研究人員發現魚類也有靈性,能用人類無法聽到的聲音溝通交流,並會互相磨擦身體以表達情感,而且擁有驚人的長期記憶力,如那些從魚網中逃生的魚,在11個月後仍記得是如何逃脫的,時間相等於人類的40年光陰。由於被人類殘害得多,經驗老到的魚兒已懂得教導年輕的魚辨認危險,如拖網魚船引擎的聲音。