Playstation 3 e o bug do ano bissexto
- por Sergio Prado
Como profissional da área de tecnologia, sou um leitor assíduo de notícias da área. Gosto em especial de dois sites, o Gizmodo e o Engadget. Na segunda pela manhã uma notícia me chamou a atenção, milhões de videogames PlayStation 3 da Sony haviam parado de funcionar da noite para o dia.
No decorrer do dia, descobriu-se que o problema estava no relógio interno do aparelho, que identificou 2010 como um ano bissexto, e passou do dia 28/02 para 29/02 ao invés de 01/03. Como diversas funções do videogame dependiam do valor correto da data do sistema para funcionar, o aparelho começou a apresentar vários problemas.
Mas antes de falar mais sobre este problema, vamos analisar um caso mais antigo, de 2008, do media player portátil Zune da Microsoft.
O caso Zune
No dia 31/12/2008, todos os Zune’s modelo 30G reiniciaram sozinhos e travaram no processo de boot do equipamento. Uma fonte descrevendo o problema podem ser encontrada aqui.
Descobriu-se depois que o problema foi causado por um bug no driver do relógio interno do equipamento, especificamente no tratamento de anos bissextos. A solução dada pela Microsoft era esperar até o fim do dia, que “milagrosamente” o problema se resolvia sozinho.
Este problema causou um barulho imenso. Mais tarde descobriu-se que o Zune usa um chip de RTC (Real-Time Clock) da Freescale, e o código-fonte do driver deste chip é aberto e disponível para download.
O código-fonte foi analisado e o trecho do código com o problema foi identificado:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
year = ORIGINYEAR; /* = 1980 */ while (days > 365) { if (IsLeapYear(year)) { if (days > 366) { days -= 366; year += 1; } } else { days -= 365; year += 1; } } |
O objetivo deste trecho de código é calcular o ano atual. Ele usa a variavel “days” que possui a quantidade de dias deste 01/01/1980, e calcula a quantidade de anos a partir de “days”, armazenando o resultado em “years”.
Em circunstâncias normais, o código executa sem problemas. Porém existe uma situação onde é ano bissexto (linha 5) porém “days” não é maior do que 366 (linha 7). Ou seja, o fluxo de execução cai dentro do if da linha 5 mas não cai no if da linha 7, e isso faz com que o código entre em um loop infinito. Como este código é executado no boot, o dispositivo trava. Esta situação acontece sempre que é ano bissexto e último dia do ano, no nosso caso – 31/12/2008.
A correção é simples, e uma possível solução é inserir uma condição de “else” após a linha 11 do código acima, conforme abaixo:
1 2 3 4 5 6 7 8 9 10 11 |
........ if (days > 366) { days -= 366; year += 1; } else { break; } ........ |
Poderia este erro ter sido evitado? Com testes simples, é bem provável que não, já que envolve uma situação bem específica (último dia de um ano bissexto). Com um bom code review, provavelmente sim. Com testes mais completos, prevendo todas as situações, com certeza o bug seria identificado.
Aparentemente, este é um driver antigo e não existe uma correção da Freescale. Então, se você tem um Zune, no dia 31/12/2012, desligue-o, vá para um mosteiro budista e relaxe!
Voltando ao caso do PS3
Este é bem mais recente, na passagem do último domingo (28/02/2010) para segunda-feira (01/03/2010) cerca de 24 milhões de aparelhos PS3 pararam de funcionar, e apresentaram os seguintes sintomas:
- Não se conectavam à rede (PSN – PlayStation Network).
- Alguns jogos não funcionavam, nem mesmo em modo offline (usando os disco ao invés de jogos baixados).
- Não era possível configurar a data/hora do equipamento.
Foi um caos. A Sony deixou muitos preocupados sem dar notícias por algumas horas. E então no meio do dia foi divulgado um comunicado pela própria Sony de que o problema havia sido identificado. Havia um bug no driver do relogio interno do equipamento, que estava reconhecendo 2010 como um ano bissexto! Ou seja, de 28/02/2010, em vez de ir para 01/03/2010, foi para 29/02/2010!
A Sony passou instruções para os usuários não ligaram o aparelho durante todo o dia, e de que no dia seguinte o sistema se normalizaria, e isso realmente aconteceu.
Mas como uma gigante como a Sony deixou que isso acontecesse? Um erro em uma rotina tão básica. Como desenvolvedor, fiquei imaginando o que poderia ter acontecido com a rotina de ano bissexto deles. Escrevi a minha (abaixo), e fiquei imaginando onde eles poderiam ter errado.
1 2 3 4 5 6 7 |
int anoBissexto(int ano) { if( (ano % 400 == 0) || (ano % 4 == 0 && ano % 100 != 0) ) return 1; else return 0; } |
Sabemos que um ano é bissexto se:
- É divisivel por 400, ou
- É divisível por 4 mas não por 100, ou
- É ano de olimpíadas
OK, o item 3 é brincadeira, apesar de ser verdade…:) Pensando bastante, cheguei a uma conclusão.
É bem provável que exista um bug no código deles onde todo ano par é considerado bissexto. Como o console foi lançado no final de 2006, este ano não foi problema. Como 2008 era um ano bissexto, também não foi problema. E agora em 2010 foi a primeira “aparição” deste problema. É claro que isso é só uma especulação minha, já que não tenho os fontes, e não vejo nenhuma outra lógica para isso ter acontecido. De qualquer forma, só pra previnir, em ano de Copa do Mundo, não liguem o PS3 no dia 1 de março!
É também interessante notar aqui como um problema simples e localizado de cálculo de ano bissexto pode prejudicar o funcionamento básico do equipamento e impactar milhões de pessoas ao redor do mundo. Além é claro de manchar um pouco a imagem da empresa. E isso aconteceu com três grandes empresas de tecnologia (Microsoft, Freescale e Sony).
OK, somos propensos a erros, mas é por isso que existem os processos de qualidade. Para garantir que, em primeiro lugar, nao coloquemos bugs no código, e se isso acontecer, que estes erros sejam identificados o corrigidos antes de chegar ao mercado.
Mas infelizmente os processos de qualidade brigam às vezes com coisas como prazos apertados e time-to-market, e nós desenvolvedores acabamos “pagando o pato” pelos problemas que poderiam ter sido evitados. Mas mesmo isso não tira de nós a responsabilidade de desenvolver um projeto de qualidade.
Um abraço a todos,
Sergio Prado