Bug della Java Virtual Machine

Non mi ero mai imbattuto in un bug della Java Virtual Machine fino alla scorsa estate. Stavo collaudando l’applicazione RouterLogger, la quale basa il suo funzionamento su un ciclo di richieste di rete che può essere infinito oppure limitato ad un numero di iterazioni configurabile. La prima implementazione prevedeva un ciclo quasi infinito:

private void loop() throws IOException, InterruptedException {

    /* Determinazione numero di iterazioni... */
    int iterations = configuration.getInt("logger.iterations", -1);
    if (iterations <= 0) {
        iterations = Integer.MAX_VALUE;
    }

    for (int iteration = 1; iteration <= iterations && !exit; iteration++) {
        /* Richiesta di rete... */

        if (iteration != iterations) {
            /* Thread.sleep(waitTimeInMillis); */
        }
    }
}

Nel caso in cui la proprietà di configurazione logger.iterations fosse stata impostata ad un valore negativo (tipicamente -1), il programma avrebbe impostato come numero di iterazioni da eseguire Integer.MAX_VALUE (ossia 2.147.483.647), determinando un ciclo praticamente infinito considerando che, nel caso di RouterLogger, un’iterazione dura normalmente qualche secondo.

Ebbene, eseguendo un test con logger.iterations=-1, notavo che il loop terminava dopo un certo numero di iterazioni (intorno a sedicimila), sempre lo stesso, e senza alcun errore o eccezione, come se la condizione di permanenza nel ciclo iteration <= iterations && !exit non fosse più verificata, cosa evidentemente falsa. Controllato il codice e verificatane la correttezza, iniziavo a non capire cosa stesse succedendo. La confusione è aumentata quando ho notato che facendo girare l’applicazione direttamente da Eclipse, quindi utilizzando il compilatore integrato in Eclipse (JDT) invece di quello del JDK 6u45, il problema non si presentava affatto. Decidevo quindi fare il reverse engineering dei due compilati (.class) per scongiurare un eventuale difetto del compilatore del JDK, ma anche quest’indagine non portava da nessuna parte: i due bytecode, pur essendo differenti, non presentavano anomalie. Tanto per curiosità, pubblico il bytecode generato prima con il JDK e poi con Eclipse, da me reinterpretato in C per maggiore chiarezza:

JDK 6u45:

14:  int iterations = 2147483647;
20:  int iteration = 0;
22:  if (iteration > iterations) goto 152;
28:  if (exit) goto 152;
35:  /* Richiesta di rete... */
62:  if (iteration == iterations) goto 146;
143: /* Thread.sleep(waitTimeInMillis); */
146: iteration++;
149: goto 22;
152: return;

Eclipse JDT:

18:  int iterations = 2147483647;
22:  int iteration = 0;
24:  goto 144;
27:  /* Richiesta di rete... */
54:  if (iteration == iterations) goto 141;
138: /* Thread.sleep(waitTimeInMillis); */
141: iteration++;
144: if (iteration > iterations) goto 157;
154: if (!exit) goto 27;
157: return;

Seppur diversi, entrambi i codici sono logicamente corretti. Escluso un problema legato al compilatore, resta quasi solo l’ipotesi di un bug della JVM. Effettivamente aggiornando il JRE alla versione 7u80, il problema scompariva del tutto, indipendentemente dal compilatore utilizzato, tuttavia mi faceva piacere mantenere la compatibilità con il JRE 6. Inoltre la cosa è inquietante perché il codice è di una semplicità unica e il comportamento della JVM risulta totalmente illogico.

Dopo alcune ricerche, ho individuato che esiste effettivamente un bug della JVM (JDK-5091921) legato ad un’ottimizzazione che viene eseguita a runtime. In pratica, a un certo momento non ben precisato durante l’esecuzione del programma, la JVM decide di ottimizzare il loop modificando la condizione di permanenza nel seguente modo: i <= j diventa i < j + 1, probabilmente perché per il processore è più semplice eseguire un controllo di minoranza stretta; la cosa non sarebbe un problema se non fosse che, nel caso j = Integer.MAX_VALUE, la somma Integer.MAX_VALUE + 1, a causa di un overflow, risulti in un bel numerone negativo (per la precisione Integer.MIN_VALUE ossia -2.147.483.648). Per questo motivo l’indice dell’iterazione, qualsiasi esso fosse, diventava immediatamente maggiore di -2.147.483.648, e il loop terminava come se niente fosse e senza alcun errore.

Per rendere il mio codice compatibile con il JRE 6, ho deciso di modificarlo rimuovendo il riferimento ad Integer.MAX_VALUE, pervenendo tra l’altro ad una soluzione più semplice e pulita:

private void loop() throws IOException, InterruptedException {

    /* Determinazione numero di iterazioni... */
    int iterations = configuration.getInt("logger.iterations", -1);

    for (int iteration = 1; (iterations <= 0 || iteration <= iterations) && !exit; iteration++) {
        /* Richiesta di rete... */

        if (iteration != iterations) {
            /* Thread.sleep(waitTimeInMillis); */
        }
    }
}

Questo bug, scoperto nel 2004 e risegnalato numerose volte alla Sun (acquisita nel frattempo da Oracle nel 2010), era stato classificato a bassa priorità (no comment), ed è stato risolto solo nel 2011 con l’aggiunta di un controllo sull’overflow. In ogni caso, per maggiore sicurezza, conviene prestare particolare attenzione quando si ha a che fare con i MAX_VALUE.

Il massacro delle date in linguaggio C

Il C ha ormai più di quarant’anni sulle spalle, ciò nonostante è ancora molto utilizzato in alcuni ambiti, devo dire sempre più ristretti. Nel corso dei decenni, ANSI e ISO hanno anche redatto delle specifiche al fine di rendere standard il linguaggio e garantirne la portabilità, e così abbiamo i vari C89/C90, C95, C99 e C11.

Coloro che hanno esperienza di programmazione potranno confermare che i tipi di dato utilizzati nella stragrande maggioranza delle applicazioni sono tre: numerico, testo e data. Consideriamo ad esempio la seguente struttura:

struct paziente {
    char cognome[100];
    char nome[100];
    char codiceFiscale[17];
    int altezza;
    float peso;
    time_t dataNascita;
};

Non era certo nelle intenzioni di Kernighan e Ritchie rendere il C adatto alla realizzazione di applicazioni gestionali; si tratta piuttosto di un linguaggio pensato per la scrittura di sistemi operativi, BIOS, driver e programmi per microcontrollori. Nulla vieta comunque di utilizzarlo per scrivere qualsiasi genere di programma.

Per quanto riguarda il trattamento dei numeri, il C non è male, tra short, int, long, float, double (e anche char), sia signed che unsigned, non c’è che l’imbarazzo della scelta; inoltre, per le operazioni più complesse è presente la libreria <math.h>.
Le strighe sono già più fastidiose, ma una volta compresi i meccanismi che le governano, non sono un problema; le librerie <string.h> e <ctype.h>, poi, forniscono un buon numero di funzioni per la loro manipolazione.

Le date, come vedremo, sono semplicemente un incubo.
Nella libreria <time.h> esiste il tipo time_t che non è altro che un intero a 32 bit con segno, il che significa che può assumere valori compresi tra -2.147.483.648 e 2.147.483.647. Come fa un numero di questo genere a rappresentare una data? Semplice: si tratta del numero di secondi trascorsi dalla mezzanotte del 01/01/1970; quest’ultima data è anche detta epoca (epoch). È chiaro che un valore del genere risulta tutt’altro che intellegibile, per cui tipicamente occorre convertirlo in una data del tipo dd/MM/yyyy HH:mm:ss. La libreria <time.h> include una struttura che risulta molto utile per questa conversione; è definita come segue:

/* A structure for storing all kinds of useful information about the current (or another) time. */
struct tm {
    int tm_sec;   /* Seconds: 0-59 (K&R says 0-61?) */
    int tm_min;   /* Minutes: 0-59 */
    int tm_hour;  /* Hours since midnight: 0-23 */
    int tm_mday;  /* Day of the month: 1-31 */
    int tm_mon;   /* Months *since* january: 0-11 */
    int tm_year;  /* Years since 1900 */
    int tm_wday;  /* Days since Sunday (0-6) */
    int tm_yday;  /* Days since Jan. 1: 0-365 */
    int tm_isdst; /* +1 Daylight Savings Time, 0 No DST, -1 don't know */
};

Come si nota, il fatto di esprimere il mese a cominciare da zero ha radici molto lontane, visto che lo ritroviamo ancora oggi nel Calendar di Java, ciò però rende semplici le operazioni sulle date. Orribile è invece la formula utilizzata per il campo tm_year.

Per convertire un valore di tipo time_t in questa struttura, si possono utilizzare le funzioni struct tm * localtime (const time_t *time) (tiene conto del fuso orario) e struct tm * gmtime (const time_t *time) (ora di Greenwich) incluse in <time.h>.
La seguente funzione, ad esempio, prende come parametro un valore time_t e restituisce una stringa contenente la relativa data/ora formattata:

char* formatDateTime(const time_t mytime) {
    char* dateTimeStr = malloc(20 * sizeof(char));
    struct tm date = *localtime(&mytime);
    sprintf(dateTimeStr, "%.2d/%.2d/%4d %.2d:%.2d:%.2d", date.tm_mday, date.tm_mon + 1, date.tm_year + 1900, date.tm_hour, date.tm_min, date.tm_sec);
    return dateTimeStr;
}

Notare l’operatore di “dereferenziazione” (*) applicato alla chiamata alla funzione localtime, la quale restituisce un puntatore alla struttura. È importante sapere che tale struttura è statica, per cui chiamate successive a localtime o gmtime modificano sempre la stessa struttura; ecco perché non è il caso di tenersi il puntatore ma è opportuno dereferenziare e copiare la struttura statica in una struttura dichiarata da noi.

C’è poi un’altra operazione importante: determinare il giusto valore di time_t a partire da una data in formato umano; il caso tipico è quando prendiamo in ingresso una data digitata dall’utente. Questa operazione è possibile grazie alla funzione time_t mktime (struct tm *brokentime) di <time.h>:

time_t newDateTime(const int year, const int month, const int date, const int hrs, const int min, const int sec) {
    time_t time;
    struct tm tmStruct = { 0 };
    tmStruct.tm_mday = date;
    tmStruct.tm_mon = month - 1;
    tmStruct.tm_year = year - 1900;
    tmStruct.tm_hour = hrs;
    tmStruct.tm_min = min;
    tmStruct.tm_sec = sec;
    time = mktime(&tmStruct);
    if (time == -1) {
        printf("Data non supportata.");
        exit(1);
    }
    return time;
}

Un grande vantaggio della struct tm è che tramite essa è possibile manipolare facilmente le date, ad esempio aggiungendo o sottraendo giorni, mesi, anni, insomma le stesse cose che in Java si possono fare con il metodo add dell’oggetto Calendar. La funzione mktime provvede infatti a normalizzare i contenuti della struttura (ad esempio 32/01 diventa 01/02):

void modificaData() {
    struct tm tmStruct;
    time_t dataDaModificare;
    time_t dataModificata;

    dataDaModificare = newDateTime(1990, 11, 12, 14, 10, 55);
    printf("Data da modificare: %s\n", formatDateTime(dataDaModificare));

    tmStruct = *localtime(&dataDaModificare);
    tmStruct.tm_mon -= 104;
    dataModificata = mktime(&tmStruct);
    if (dataModificata == -1) {
        printf("Data non supportata.");
        exit(1);
    }
    printf("Data modificata: %s\n", formatDateTime(dataModificata));
}

Fin qui la situazione sembrerebbe più che accettabile, ma purtroppo non è così.
Il tipo time_t presenta un grande problema, talmente grave da essere stato citato addirittura da John Titor (si scherza, ovviamente), e conosciuto come bug dell’anno 2038. Dato l’intervallo di valori gestiti, il tipo time_t nella migliore delle ipotesi non può rappresentare date successive al 19/01/2038, né precedenti al 13/12/1901. Considerando inoltre che non tutti i sistemi accettano valori negativi per questo tipo, non è raro che non siano rappresentabili date precedenti al 01/01/1970; è evidente che in quest’ultimo caso, il tipo time_t non va bene nemmeno per rappresentare date di nascita di persone attualmente in vita.

Possibili soluzioni

Tra le possibili e più o meno retrocompatibili vie d’uscita, vi è l’adozione di un intero a 64 bit al posto di quello attuale; il progetto y2038 è basato proprio su quest’idea, e consiste in una nuova libreria "time64.h" che estende quella standard. È interessante come, nel caso di date che ricadano nell’intervallo sicuro (13/12/1901-19/01/2038), questa libreria deleghi tutte le operazioni all’originale <time.h>.
Purtroppo però non mancano i problemi: uno di essi è che le operazioni sulle date non vengono sempre eseguite come ci si aspetterebbe; in particolare, se si tenta di modificare una data che non ricade nell’intervallo sicuro operando sui campi della relativa struct tm, la nuova funzione mktime64 non aggiorna correttamente il campo dell’anno, ad esempio sommando 12 al campo dei mesi, l’anno non viene incrementato; in pratica l’effetto è simile a quello del metodo roll del Calendar di Java.
Altro grande problema è che l’algoritmo che verifica se una data ricade nell’intervallo sicuro non sempre opera correttamente: se ad esempio si parte da una data che effettivamente ricade nell’intervallo sicuro (ad es. 01/01/2038) e gli si somma un valore tale per cui il risultato ricadrà al di fuori dell’intervallo sicuro (ad es. aggiungendo 6 al campo tm_mon della struct tm relativa alla data di partenza), la libreria delega comunque le operazioni all’originale <time.h> che ovviamente fallisce.

In alternativa, è sempre possibile gestire la data e l’ora come valori interi separati (giorno, mese, anno, ore, minuti e secondi), magari sotto forma di struttura (la stessa struct tm potrebbe andare bene) senza passare per time_t; ma ancora una volta ciò rende complicate le operazioni sulle date, le quali devono essere implementate manualmente.

Articolo consigliato: Time, Clock, and Calendar Programming In C, di Eric S. Raymond.

È uscito il Sierra-Bates per la certificazione Java SE 7 Programmer

Dopo un’attesa durata diversi anni, ora che è già possibile certificarsi addirittura per la Java Standard Edition 8 (esame 1Z1-808), è stata finalmente data alle stampe la Study Guide (il libro di testo) per le certificazioni Oracle Java Standard Edition 7 Programmer di Kathy Sierra e Bert Bates, gli storici autori di molte delle precedenti guide ufficiali alle certificazioni Java di Sun e, successivamente, Oracle.
Nella pagina del prodotto sul sito della Oracle Press sono disponibili per il download gratuito il sommario e il capitolo 6 “Flow Control and Exceptions” in formato PDF.

Copertina del libro "OCA/OCP Java SE 7 Programmer I & II Study Guide (Exams 1Z0-803 & 1Z0-804)"

Non sono ancora disponibili i Practice Exams (simulazioni d’esame non al calcolatore, con soluzioni dettagliate) curati dagli stessi autori, la cui uscita è prevista non prima della nostra prossima vita (pare per il 2016, ma la data è già stata spostata più volte, ovviamente sempre in avanti). Si può comunque rimediare a questa mancanza con le numerose simulazioni d’esame disponibili online; tra le migliori si segnalano quelle curate da Enthuware, che ben ricalcano i quesiti e la grafica dell’esame reale.

A seguito dell’acquisizione di Sun Microsystems da parte di Oracle, è stata messa in atto una ristrutturazione del sistema di certificazioni Sun; Oracle ha infatti ereditato da Sun le certificazioni Java e le ha inserite nel proprio catalogo attribuendovi i livelli e, ultimamente, alcune propedeuticità già in uso per le altre certificazioni Oracle esistenti.

Oracle Certification Program

Nel programma di certificazione Oracle vi sono attualmente cinque categorie di certificazione: Associate, Professional, Expert, Master (in ordine di rilevanza) e Specialist (non presente tra le certificazioni Java). Sovente si può accedere al livello successivo solo se si possiede già un titolo del livello precedente, ma esistono delle eccezioni. Attualmente infatti è possibile diventare Oracle Certified Professional Java SE 5 o 6 sostenendo un solo esame, tuttavia Oracle ha stabilito che, a partire dalla versione 7, occorre superarne due: prima quello per il livello Associate e poi quello per il livello Professional.
Detto ciò, e constatato che certificarsi per la vecchia versione 5 è inutile, un’idea per chi non possiede nessuna certificazione potrebbe anche essere quella di orientarsi inizialmente sulla versione 6, in modo da ottenere, con un solo esame, un attestato Professional subito spendibile, sostenendo eventualmente successivamente solo l’esame di aggiornamento alla versione 7 (quello per la versione 8 è ancora in versione beta e solo per il livello Associate).

Ricapitolando, gli esami per la versione 7 sono dunque 1Z0-803 (Associate), 1Z0-804 (Professional) e 1Z0-805 (Professional, aggiornamento per candidati con precedente certificazione Java SE, anche rilasciata da Sun). Per la versione 6, invece, l’esame unico è 1Z0-851 (Professional), che corrisponde al vecchio 310-065 (numerazione Sun), e il materiale ufficiale di Sierra e Bates per prepararlo è ampiamente disponibile e collaudato da anni, si tratta della Study Guide (libro + CD) e dei Practice Exams.

Copertina della Study Guide per l'esame 1Z0-851 Copertina dei Practice Exams per l'esame 1Z0-851

Ultimo suggerimento: le edizioni in formato eBook dei manuali citati sono da evitare per via di possibili problemi di visualizzazione che potrebbero complicarne la fruizione; sono infatti presenti diverse immagini, molto codice Java e numerose tabelle che poco si prestano ad essere traslate in formati diversi dal PDF.

Link utili:

Stack: Java e C a confronto

Lo so, è un’impresa persa, del resto i due linguaggi sono molto diversi tra loro, però vorrei mettere in evidenza un fatto piuttosto evidente che mi è capitato di notare. Riguarda l’uso dello stack.

Java

Consideriamo la seguente classe, concepita esclusivamente per testare lo stack:

public class StackUsage {

    private static long calls;
    
    public static final void main(String... args) {
        StackUsage su = new StackUsage();
        su.test();
    }

    private void test() {
        calls++;
        try {
            test();
        }
        catch (StackOverflowError soe) {
            System.out.println("Totale chiamate: " + calls);
            System.exit(0);
        }
    }
}

L’invocazione ricorsiva del metodo test() senza alcuna condizione di uscita ci garantisce l’esplosione dello stack, la quale puntualmente si verifica. Effettuando il catch dell’errore (cosa che normalmente non è bene fare), possiamo farci stampare sulla console il numero delle chiamate che sono state eseguite. Nel mio caso sono quasi sempre 10.480. Ce lo appuntiamo.

Modifichiamo ora leggermente il metodo test() aggiungendo una variabile locale, in questo caso di tipo long:

    private void test() {
        long a = 1234567890L;
        calls++;
        try {
            test();
        }
        catch (StackOverflowError soe) {
            System.out.println("Totale chiamate: " + calls);
            System.exit(0);
        }
    }

Eseguendo questa nuova versione del programma, viene fuori un numero un po’ diverso di chiamate, nel mio caso 8.903. La differenza si spiega facilmente: per ogni chiamata al metodo viene messo da parte il contenuto della variabile long a, in modo che, al ritorno, possa essere ripristinato il corretto stato dell’applicazione. Fin qui tutto torna.

Modifichiamo ancora una volta il metodo, stavolta eliminando l’inizializzazione della variabile long. La riga quindi diventerà:

        long a;

Come si nota, la variabile non viene mai utilizzata, in caso contrario la classe non compilerebbe. Ci aspettiamo a questo punto un numero di chiamate maggiore del caso precedente, più o meno lo stesso del caso in cui la variabile non veniva dichiarata, dato che non c’è alcunché da memorizzare nello stack.
Se eseguiamo questo codice otteniamo invece più o meno lo stesso risultato del caso in cui la variabile veniva inizializzata. Cosa succede? La verità è che le dimensioni dello stack frame che dovrà contenere i dati da salvare al momento dell’invocazione di un metodo vengono calcolate a tempo di compilazione e inserite direttamente nel file .class, ma il calcolo della memoria richiesta viene eseguito senza considerare se le variabili vengono inizializzate o meno.

C

Effettuiamo ora lo stesso esperimento in linguaggio C.

Nota bene: il codice che segue DEVE essere eseguito solo in un ambiente di test virtuale e isolato (macchine virtuali, anche DOSBox) poiché può provocare il blocco completo del sistema con conseguenti perdite di dati.

Iniziamo dalla versione senza variabile locale:

#include <stdio.h>

long int calls = 0;

void test(void) {
    calls++;
    if (calls % 10 == 0)
        printf("%d ", calls);
    test();
}

int main(void) {
    test();
    return 0;
}

In questo caso dobbiamo farci stampare il numero di chiamate di tanto in tanto (ogni decina in questo caso) poiché quando in C lo stack esplode, esplode veramente e non c’è niente da fare. Il programma si fermerà quando lo stack sarà pieno e probabilmente stamperà anche un po’ di spazzatura a video dopo l’ultimo numero che indica il totale delle chiamate effettuate, che nel mio caso ammonta a circa 16.110.

Modifichiamo ora la funzione test() aggiungendo una variabile locale di tipo long int:

void test(void) {
    long int a = 1234567890L;
    calls++;
    if (calls % 10 == 0)
        printf("%d ", calls);
    test();
}

Rieseguiamo e il risultato è circa 8.090; tutto normale e ampiamente prevedibile.

Analogamente a quanto fatto sopra, eliminiamo ora l’inizializzazione della variabile locale:

    long int a;

Ricompiliamo ed eseguiamo. Risultato? Di nuovo 16.110, come quando la variabile non era dichiarata.

Si deduce quindi che in C le dimensioni dello stack frame vengono determinate a tempo di esecuzione, con conseguente risparmio di memoria nel caso di variabili dichiarate ma non inizializzate.

Ulteriori informazioni: JavaworldOracle

Tablet uguale Netbook?

Netbook Tablet

Immagini tratte da www.amdplanet.it (netbook Asus) e www.androidiani.com (tablet Samsung).

Forse solleverò un vespaio, ma ho appena letto una notizia che è stata la goccia che ha fatto traboccare il (mio) vaso: “Netbook verso l’estinzione: spariscono scalzati dai tablet”; in breve, Asus e Acer, i principali produttori di netbook, annunciano che entro quest’anno cesseranno la produzione di questi piccoli ed economici PC portatili. Motivo? Scalzati dai tablet, dicono. Ecco, è proprio qui il problema: si tenta di paragonare due dispositivi molto diversi, e non è questo l’unico caso.

I netbook hanno un’architettura analoga a quella dei PC e completamente diversa da quella dei tablet; hanno una tastiera, anche se qualche tablet evoluto ne dispone (vedi Asus serie Transformer), un hard disk o comunque una memoria di massa di dimensioni considerevoli, spesso di un ordine di grandezza superiori a quelle di un tablet, e permettono di far girare tutte le applicazioni che normalmente usiamo già sui nostri PC; quest’ultima cosa, così semplice, i tablet non la permettono: richiedono infatti applicazioni appositamente sviluppate che, se esistenti, andranno necessariamente ricomprate se a pagamento. Per non parlare di tutte le altre limitazioni alle quali si deve sottostare quando ci si trova ad operare con un tablet: il sistema operativo è imposto e si è in balia dei suoi aggiornamenti, spesso proprietari (distribuiti dalla casa costruttrice del tablet invece che dalla software house del sistema operativo) e limitati nel tempo; in molti casi si possono installare solo applicazioni provenienti da questo o quel market o store che dir si voglia, collegare una qualunque periferica USB spesso non è affatto immediato, e via dicendo. Tutto ciò naturalmente a meno di non operare modifiche a basso livello assolutamente non supportate dalla casa produttrice, come l’installazione di versioni del sistema operativo modificate da terzi. Le differenze dunque ci sono, e sono notevoli; certo chi utilizzava il PC per navigare il web, gestire la posta elettronica e magari giocare ogni tanto al solitario certamente si troverà anche meglio con un tablet, il quale, vale la pena di ricordarlo, mediamente costa più di un netbook; ma per chi ha bisogno di qualcosa in più?

Qualche settimana fa, un’altra notizia: “Pc in crisi, sostituiti da smartphone e tablet”; ora, al di là dell’ennesimo paragone tra le mele e le pere, purtroppo non posso non scorgere uno scenario futuro preoccupante: vedo un mercato con i prezzi dei PC portatili in decisa crescita e con quelli dei tablet stabili, vedo un progressivo disinteresse nei confronti della storica piattaforma PC da parte delle software house e dei produttori di hardware, vedo un dominio di questa dittatura tecnologica fatta di vincoli e restrizioni, di dispositivi costosi che godono di un supporto limitato a pochissimi anni (Windows XP viene supportato da 12 anni, dodici!). Vedo un futuro in cui, per reinstallare, aggiornare, sostituire il sistema operativo bisognerà prima tentare escludere quello esistente, un po’ come succedeva con HAL 9000 in 2001: Odissea nello spazio; siamo nel 2013, quel genio di Kubrick avrà sbagliato solo di pochi anni.

Hard Disk Verbatim e settori danneggiati

Verbatim Hard Disk USB 2.5"

VerbatimRiporto di seguito due scambi di email riguardanti un problema con un hard disk esterno USB Verbatim da 2.5": il primo è con il supporto tecnico europeo, il secondo è con il supporto tecnico italiano.

Supporto europeo

Me

[to: drivesupport@verbatim-europe.com]

Greetings.

I bought a Verbatim 640 GB 2,5" hard drive 8 days ago and I ran a Scandisk on the drive in order to be sure that it was error free, but the check returned 176 KB as bad clusters, as you can see on the attachment scandisk.png. So I ran also HD Tune and the SMART status reported 3 warnings (hdtune.png attachment).

Seller said that there is no replace if percentage of bad sectors is less than 10%, but I’m not sure of this threshold, I never hear of it. I hope you could help me.

Verbatim Support

[from: drivesupport@verbatim-europe.com]

Dear Customer,

Thank you for this e-mail. The reseller is right. Less than 10% of bad clusters is accepted by the industry.

This drive cannot be replaced. It is normal to have a small number of faulty clusters on a drive.

Best Regards,
Verbatim Euro Support

Me

[to: drivesupport@verbatim-europe.com]

Dear Verbatim Support,

I think it is not acceptable for a customer to buy a drive of a certain size and to be able to use only 90% of its capacity in the worst case.

Following your argument, an unfortunate customer could be forced to keep a brand new 640 GB defective drive with a loss of up to 60 GB, having paid for all the 640 GB.

However reseller has performed an extended check of my drive during the last two days, and about 200 errors resulted.

Of course I’ll not use that defective drive because in my opinion its reliability is seriously compromised.

Thank you.

Supporto italiano

Me

[to: info.italy@verbatim-europe.com]

Salve,

Ho acquistato un hard disk portatile Verbatim 640 GB 2,5″ USB; qualche giorno fa ho eseguito uno scandisk da Windows tanto per essere certo di avere un disco integro; il controllo ha riportato invece 176 KB in settori danneggiati (cfr. allegato scandisk.png). A quel punto ho eseguito anche HD Tune per avere ulteriori informazioni e il programma riporta 3 SMART Warning (cfr. allegato hdtune.png).

Mi sono rivolto al rivenditore (visto l’acquisto recentissimo) il quale mi ha detto che non può sostituire l’unità in quanto c’è una soglia del 10% di cluster danneggiati sotto la quale Verbatim non effettua la sostituzione del prodotto. Personalmente non credo che esista una tale soglia e quindi spero possiate aiutarmi voi.

Verbatim Support

[from: info.italy@verbatim-europe.com]

Gent.mo Cliente,

Le confermo che esiste il vincolo che stabilisce la quantità minima di cluster danneggiati al di sotto della quale non si effettua la sostituzione del prodotto.

La presenza di pochi cluster danneggiati (176 KB nel suo caso) non dovrebbe pregiudicare la funzionalità del dispositivo.

Nel caso in cui, in futuro, dovesse riscontrare problemi può contattare il nostro supporto tecnico.

Me

[to: info.italy@verbatim-europe.com]

Se davvero la soglia di settori danneggiati è il 10% (come per altro il Vs. supporto tecnico mi ha confermato via email in lingua inglese […]), vorrebbe dire che potrebbe capitare ad uno sfortunato utente di ritrovarsi un disco nuovo di una certa capacità nominale, diciamo 640 GB, con, nel caso peggiore, solo 576 GB disponibili, pur avendo pagato tutti i 640 GB indicati sulla confezione; lascio a Lei le debite conclusioni. Sottolineo che di detta soglia non mi pare si faccia menzione chiara da nessuna parte nel vostro sito, tantomeno sulla confezione dell’unità, sicché prima dell’acquisto non v’è modo di saperlo.

Il negoziante, al quale mi sono rivolto subito dopo averVi scritto, dopo aver effettuato dei controlli e appurato il difetto da me riscontrato si è offerto di sostituirmi il disco a sue spese (cosa che lui ha specificato e che non mi sorprende data la risposta del Vs. supporto), permettendomi di scegliere anche un’unità di altra marca.

Dischi con settori danneggiati e con degli SMART alert a mio avviso non sono affidabili (dunque non arrischierei mai i miei dati archiviandoveli).

Allegati

Screenshot di HD Tune riportante errori SMART Screenshot della console con il rapporto di CHKDSK riportante 176 KB in settori danneggiati
hdtune.png [PNG – 78 KB] scandisk.png [PNG – 19 KB]

Per completezza desidero puntualizzare due fatti:

  1. Verbatim non fabbrica hard disk ma li acquista da terze parti e li “inscatola”, offrendo naturalmente il supporto tecnico. L’unità da 640 GB di cui si parla nelle email incorporava un drive di fabbricazione Toshiba.
  2. Possiedo un altro disco Verbatim USB dello stesso tipo ma da 500 GB che non mi ha mai dato nessun tipo di problema. Quest’altra unità incorpora un drive prodotto da Western Digital.

verbatim settori danneggiati soglia 10% garanzia 10 percent damaged cluster clusters warranty replace hard disk drive chkdsk

Installare SQL Navigator 6 su Windows a 64 bit

Icona SQL NavigatorSQL Navigator è un ambiente integrato per lo sviluppo e l’ottimizzazione di procedure, funzioni e package PL/SQL su piattaforma Oracle; permette di eseguire statement e script SQL e di operare su tutti gli oggetti del database. L’interfaccia grafica di cui dispone è simile a quella di Oracle SQL Developer, del quale, detto per inciso, è stata rilasciata tre giorni fa la versione 3.0, disponibile anche per sistemi a 64 bit (necessita del JDK x64); chiusa parentesi.

Windows XP

L’installazione di SQL Navigator sul collaudatissimo Windows XP normalmente non presenta alcuna difficoltà, l’importante è avere installato il client di Oracle, necessario per collegarsi al database. Il modo più semplice e rapido per installare il client è scaricare Oracle Database Express Edition; al termine dell’installazione è possibile disabilitare i 5 servizi di Oracle (Start > Esegui… > services.msc) se si è interessati solo al client e si vuole risparmiare RAM. Le versioni di SQL Navigator che ho provato personalmente su XP sono la 5.1 e le successive.

Windows 7 x86

Per quanto riguarda invece l’installazione su Windows 7, le versioni da me testate sono la 6.1 e la 6.2. L’ultima versione disponibile è la 6.4, che tra l’altro è ufficialmente compatibile con Windows 7. Installando la versione 6.1 su Windows 7 x86 (sistema a 32 bit), il sistema operativo rileva che l’applicazione è obsoleta e attiva una modalità di compatibilità (riconoscibile dal simbolo Icona compatibilità sull’icona del programma) che comunque risulta totalmente trasparente all’utente, infatti il programma si avvia ed è utilizzabile anche se presenta qualche saltuaria anomalia risolvibile riavviandolo. La versione 6.2 funziona meglio ed è sicuramente da preferire: non presenta il simbolo della modalità compatibilità e fino ad ora non ho riscontrato alcun problema durante l’utilizzo. Ricordo che anche con Windows 7 occorre avere installato il client di Oracle.

Windows 7 x64

L’installazione su Windows 7 x64 (64 bit) richiede invece qualche attenzione in più. Installando infatti sia la versione 6.1 che la 6.2 con le opzioni di default, il programma si avvia (la versione 6.1 presenta sempre il simbolo Icona compatibilità sull’icona) ma non è in grado di collegarsi a nessun database, restituendo il messaggio di errore ORA-12154 che non aiuta ad individuare il problema. Di primo acchito sembrerebbe trattarsi di una qualche oscura incompatibilità con il nuovo sistema operativo ma fortunatamente non è niente di tutto questo; il fatto è che Windows 7 x64 adotta come percorso di installazione dei programmi a 32 bit \Programmi (x86) e non \Programmi, cosa che SQL Navigator, applicazione a 32 bit, non digerisce proprio. E infatti, installando il programma in un percorso senza le parentesi, ad esempio nella directory principale (\Quest Software\ invece di \Programmi (x86)\Quest Software\), tutto funziona regolarmente.
In caso si riscontrassero ulteriori problemi, verificare che il client di Oracle sia installato e che ovviamente non ci siano problemi di rete.

Per approfondimenti consiglio la consultazione di questo forum.