Monday, February 27, 2012

How to dump PHP stack from gdb

The case:
  1. Php-based applications running on a linux server, be it under httpd or php-cgi process
  2. One of the applications misbehave, i.e. page never displayed until timeout
  3. Timeout mechanism doesn't work or we're unable to pinpoint the problem from the timeout message written on the logfile

We would like to dump PHP stack traces from a running PHP script.

Required tool: xdebug, gdb.

Using standard xdebug functionality, debugging is only possible when php debugging explicitly initiated (eclipse/phpstorm must be running and the page is requested with debugging key activated). Just in time debugging in xdebug means debug when an error occured, which is not always the case. Remote_autostart means to debug in every time a php page is requested, which is an overkill in my opinion.

Procedure :

  • obtain list of pids from processes we're interested in (ps auxw | grep httpd)
  • guess which pid (or iterate pids)
  • run gdb, attach to the pid (gdb --pid=nn)
  • run this gdb script (i.e source dumpphpstack.txt)

dumpphpstack.txt : (tested on x86_64 architecture, CentOS 6)

set $xstack = ((long**)&xdebug_globals)[2]
set $pcurrent = (long*)$xstack[0]
while $pcurrent
set $xptr = (long*)$pcurrent[0]
set $xptr_s = (char**)$xptr
set $xptr_i = (int*)$xptr
set $filename = $xptr_s[4]
set $funcname = $xptr_s[1]
set $linenum = $xptr_i[10]
printf "%s@%s:%d\n", $funcname, $filename, $linenum
set $pnext = (long*)$pcurrent[2]
set $pcurrent = $pnext
end

This script may need adjustment because architectural difference or field alignment variations. Now if just someone could teach me how to iterate pids via shell script, I could've create the first PHP sampling profiler..

Wednesday, January 4, 2012

OSGI commons-logging blues

I read that OSGI plugins is difficult to build because of the isolation built into the plugin ecosystem. But only recently I found out that it is really troublesome, tooks me a lot of time to figure these out:
- Import-Package clause in the plugin could import already loaded classes in the main application onto the plugin. But the precondition is that the main application must already register the already loaded class packages (Java packages, not maven artifactId) onto the plugin ecosystem.
- Imported classes could refer to other classes. Runtime exception could occur unless the other class packages already registered by the main application (ex. in the Felix configuration map) (or exported by other plugin) AND the other class packages is imported in one of the Import-Package clause.
- The Apache commons-logging library is very much not compatible with OSGI

In my case, My plugin has a spring-jdbc library dependency. At first I OSGI-imported spring packages from the main application, only to find out that the main application missed some packages, resulting in Runtime exceptions inside spring instances. Then I forgo the spring import stuffs, making them included dependency instead.
Now I am faced by commons-logging errors.

The final solution is to include spring libraries by transitive dependency but exclude commons-logging, and add SLF4J dependency clauses to replace commons-logging classes :


...
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<extensions>true</extensions>
<configuration>
<instructions>
<!-- Change package and plugin class here -->
<Export-Package>telkom.isc</Export-Package>
<Private-Package>telkom.isc.*</Private-Package>
<Bundle-Activator>telkom.isc.AuditPluginActivator</Bundle-Activator>
<Import-Package>!*,org.joget.report.dao,org.joget.report.model,org.joget.report.service,org.joget.commons.util,org.joget.plugin.base,org.joget.plugin.property.model,org.joget.plugin.property.service,org.joget.directory.model,org.joget.directory.model.service,org.joget.directory.dao,org.joget.workflow.model,org.joget.workflow.model.dao,org.joget.workflow.model.service,org.joget.workflow.util,org.joget.apps.app.dao,org.joget.apps.app.lib,org.joget.apps.app.model,org.joget.apps.app.service,org.joget.apps.datalist.lib,org.joget.apps.datalist.model,org.joget.apps.datalist.service,org.joget.apps.form.lib,org.joget.apps.form.dao,org.joget.apps.form.model,org.joget.apps.form.service,org.joget.apps.list.service,org.joget.apps.userview.lib,org.joget.apps.userview.model,org.joget.apps.userview.service,org.joget.apps.workflow.lib,javax.servlet,javax.servlet.http,org.osgi.framework;version="1.3.0"</Import-Package>
<!-- End change package and plugin class here -->
<Embed-Dependency>scope=compile|runtime;inline=false;artifactId=!commons-logging</Embed-Dependency>
<Embed-Transitive>true</Embed-Transitive>
<Embed-Directory>dependency</Embed-Directory>
<Embed-StripGroup>true</Embed-StripGroup>
<DynamicImport-Package>*</DynamicImport-Package>
</instructions>
</configuration>
</plugin>
</build>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.4</version>
<scope>test</scope>
</dependency>


<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring</artifactId>
<version>2.5.6.SEC02</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>2.5.6.SEC02</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>1.2.6</version>
</dependency>
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.3</version>
</dependency>
<dependency>
<groupId>commons-pool</groupId>
<artifactId>commons-pool</artifactId>
<version>1.5.4</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl104-over-slf4j</artifactId>
<version>1.4.2</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.4.2</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.4.2</version>
</dependency>
<dependency>
<groupId>org.joget</groupId>
<artifactId>wflow-core</artifactId>
<version>3.0-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc14</artifactId>
<version>10.2.0.2.0</version>
<scope>runtime</scope>
</dependency>
<!-- Change plugin specific dependencies here -->

<!-- End change plugin specific dependencies here -->
</dependencies>


Thursday, October 6, 2011

XNB File format

00: 'X'
01: 'N'
02: 'B'
03: 'w'
04-05 : 8004h -> compressed stream, 0004h -> uncompressed stream
06-09: compressedSize
0A-0D: decompressedSize
0E: start of data

Wednesday, August 17, 2011

Compress-a-sequence

During my attempt to learn LISP/Clojure, I tried the problems in www.4Clojure.com

4Clojure problem #30 :

Write a function which removes consecutive duplicates from a sequence.

so that
(= (__ [1 1 2 3 3 2 2 3]) '(1 2 3 2 3))
and
(= (__ [[1 2] [1 2] [3 4] [1 2]]) '([1 2] [3 4] [1 2]))

My solution :


;; ywidyatama's solution to Compress a Sequence
;; https://4clojure.com/problem/30

(fn dedupstr2 [s]
(reduce
(fn dedup-reduce [seq_or_n1 n2]
(let [lastn1
(if (seq? seq_or_n1)
(last seq_or_n1)
seq_or_n1
), seq1 (if (seq? seq_or_n1) seq_or_n1 (seq [seq_or_n1])) ]
(if (= n2 lastn1)
seq1
(concat seq1 (seq [n2])))
))
s
)
)

Wednesday, June 15, 2011

Make Rational Team Concert URLs behave

When Rational Team Concert sends email, or show a dependency link to another work item, it uses the hostname in the URL. Imagine that I installed Rational Team Concert in a sandbox server in our intranet, and without any need to register the sandbox server to the DNS. When RTC server sends email, and clicked by any of our programmers, the URL pops up with the hostname - but of course the hostname is not registered in the DNS so we have server not found error.

Recent googling turned out this IBM page that may be of assistance. And I use the /jazz variation of the URL, even tough the page says otherwise. Here is the excerpt from the page :

  1. Open a browser and log in as a member of the JazzAdmins group to the Admin Web UI on the Jazz Team Server:
    • For IBM® Rational® Team Concert, typically the URL for the Admin Web UI is https://host-name:9443/rtc/admin. In the Admin Web UI, click the Server tab.
    • For IBM Rational Quality Manager, typically the URL for the Admin Web UI is https://host-name:9443/rqm/admin. In the Admin Web UI, click the Server tab.
    • For IBM Rational DOORS Requirements Professional, typically the URL for the Admin Web UI is https://host-name:9443/jazz/admin. In the Admin Web UI, click the Server tab.
  2. In the Configuration pane, click Advanced Properties.
  3. Search for Public URI Root.
  4. In com.ibm.team.repository.service.internal.ServerDescriptionService, click Edit.
  5. In the Public URI Root property at the Current Value field, enter the URL
In the URL to be entered, I use https://10.2.3.4:9443/jazz (where 10.2.3.4 is the sandbox server's IP address)

Sunday, November 21, 2010

Installing oci8 on Centos

Today I have the inclination to resume one of my postponed to-dos, that is to install Oracle instant client and php extension oci8 in two production servers.

references :
http://shamuntoha.wordpress.com/2010/04/12/centos-oracle-php-database-connect-oci-instantclient-oci8/

http://ubuntuforums.org/showthread.php?t=92528

http://www.jagsiacs.co.uk/node/77

Overview
The basic steps are, download and extract basic instant client and SDK instant client. Then do a pecl install oci8 to download and compile oci8 extension. There are few issues I encountered when installing php oci8 extension.
  1. pear tried to connect directly to pear server. Must set http_proxy using pear config-set command.
  2. missing links. Because I downloaded zip files, and not rpms, there are few missing links when trying to link oci8 with instant client. The solution is to create them manually
  3. intermittent compilation problem. One of two compile result in an error, even with exactly the same arguments and environment condition. This means, if it fails, retry at least once, there is a chance that the second compile will work smoothly.
  4. SElinux issues. Must do some chcon to allow the execution of instant client libraries. See reference #3.
Pear proxy configuration
pear config-set http_proxy http://10.x.x.x:8080

Missing link solution
Do these commands when current working directory is where the oracle instant client library were extracted.
ln -s libclntsh.so.10.1 libclntsh.so
ln -s libocci.so.10.1 libocci.so


Selinux issue solution
I do these commands to allow execution of oracle libraries from PHP environment :
chcon system_u:object_r:textrel_shlib_t *.so
chcon system_u:object_r:textrel_shlib_t *.10.1

But these might not be enough, I only phpinfo results to confirm that oci8 is indeed loaded by PHP environment. Stay tuned for more info after we try this configuration.

Wednesday, October 6, 2010

Solving time drift problem on Ubuntu VMWare Guest

I have an Ubuntu VMWare guest, and having trouble with time drift.
After a bit twiddling with ntp synchronization and still getting unacceptable time drift,
finally I read a post in http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=471784 :


From:
Kazuhiro NISHIYAMA
To: 471784@bugs.debian.org
Subject: Please recommends open-vm-source
Date: Fri, 28 Mar 2008 21:13:22 +0900
I had a same problem, and resolved following commands:

* sudo aptitude purge open-vm-tools
* sudo reboot
* sudo aptitude install open-vm-source
* pager /usr/share/doc/open-vm-source/README.Debian
* sudo module-assistant prepare open-vm
* sudo module-assistant auto-install open-vm
* sudo aptitude install open-vm-tools
* sudo reboot

I done the steps above, and it seems to work perfectly.
Oh, I didn't do the reboot parts nor the pager one.

EDIT: Seems that its not enough. I have a AMD Phenom x4 CPU, which have some anomalies in regard to CPU clock speed.
The problem is, the CentOS 4.6 Host detected that the CPU have clock speed 1100MHz, in reality the CPU have 2200MHz clock. So, imitating this post, I must tell VMware server the real maximum clock:

vi /etc/vmware/config

edit the file so it resembles these lines:

host.cpukHz = 2200000
host.noTSC = "TRUE"
ptsc.noTSC = "TRUE"



And make sure ntpd runs to handle the small drifts that still occurs.