EURO symbol, sqlplus, cmd.exe and various issues

One customer reported a not-correctly displayed Euro Symbol (€) in the database from sqlplus (msdos).

Why?

First, the character set did not support it.


select * from v$nls_parameters where PARAMETER like '%CHARACTERSET%';

PARAMETER                      VALUE
------------------------------ ---------------
NLS_CHARACTERSET               WE8ISO8859P1
NLS_NCHAR_CHARACTERSET         AL16UTF16

If you are still using WE8ISO8859P1, consider migrating to WE8MSWIN1252 using csalter


sqlplus "/ as sysdba" @?/rdbms/admin/csminst
csscan "'sys/sys as sysdba'" full=y tochar=we8mswin1252 array=1024000 process=5
sqlplus "/ as sysdba" @?/rdbms/admin/csalter.plb

It is not always that straight forward, check output from csscan (scan.*) carefully before running csalter.

Ok, now retry


H:\>set NLS_LANG=american_america.we8pc850

H:\>sqlplus.exe scott/tiger

SQL*Plus: Release 11.1.0.6.0 - Production on Thu May 10 11:28:01 2012

Copyright (c) 1982, 2007, Oracle.  All rights reserved.

Connected to:
Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options

SQL> select chr(128) from dual;

C
-
■

Not good!

Obviously, the PC850 client character is not good enough. Let’s switch to mswin1252 on the client.


H:\>chcp 1252
Active code page: 1252

H:\>set NLS_LANG=american_america.we8mswin1252

H:\>sqlplus.exe scott/tiger

Connected to:
Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options

SQL> select chr(128) from dual;

C
-
Ç

Well, what’s missing now? The font ! Let’s change it from “Raster Fonts” to “Lucida Console”. Either by clicking on the command com properties, or even dynamically with that gem (tested on XP) !


H:\>type Lucida.cs
using System;
using System.Runtime.InteropServices;

public class Lucida
{
  const int STD_OUT_HANDLE = -11;

  [DllImport("kernel32.dll", SetLastError = true)]
  static extern int SetConsoleFont(IntPtr hOut, uint dwFontSize);

  [DllImport("kernel32.dll", SetLastError = true)]
  static extern IntPtr GetStdHandle(int dwType);

  public static void Main()
  {
    SetConsoleFont(GetStdHandle(STD_OUT_HANDLE), 6);
  }
}

H:\>csc Lucida.cs
Microsoft (R) Visual C# 2010 Compiler version 4.0.30319.1
Copyright (C) Microsoft Corporation. All rights reserved.
H:\>Lucida

H:\>sqlplus.exe scott/tiger

Connected to:
Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options

SQL> select chr(128) from dual;

C
-
€

Storage Replication for Oracle Database and Licensing

While doing my high availability deep dive at Collaborate 12 few weeks ago, I stated that storage replication qualifies for the cold failover licensing rules (see slide #128). During collaborate I spoke to one person at Oracle who definitely knows the rules. Simon Haslam also reached out to me by email pointing out that things [...]

Why did that report run slow? ASH says log file sequential read

“Dump Online Redo for Buffers in Pin History”

I’ve just been investigating why a certain report ran slow last night.

Got to be a bit careful with ASH once it goes into the repository – DBA_HIST_ACTIVE_SESS_HISTORY – because it’s a sample of a sample.

Normally, I prefer looking at the raw data to see what is and isn’t in the sample, time gaps, etc, but in this case an aggregation is quite clear:

SQL> select sql_exec_id
  2  ,      sql_plan_line_id plan_line
  3  ,      decode(event,null,session_state,event) event
  4  ,      count(*)
  5  ,      to_char(min(sample_time),'HH24:MI') min_time
  6  ,      to_char(max(sample_time),'HH24:MI') max_time
  7  ,      round(sum(delta_time)/1000000,2) delta 
  8  from   dba_hist_active_sess_history
  9  where  sql_id = '2b1y41hau5qhr'
 10  and    sample_time >= trunc(sysdate) -1
 11  group by trunc(sample_time), sql_exec_id, sql_plan_line_id, event
 12  ,      session_state
 13  order by 5 asc;

SQL_EXEC_ID  PLAN_LINE EVENT                            COUNT(*) MIN_T MAX_T      DELTA
----------- ---------- ------------------------------ ---------- ----- ----- ----------
                       control file sequential read            6 19:57 20:19      58.83
                       log file sequential read              289 19:58 20:48    2897.27
                       ON CPU                                 11 20:01 20:47     110.27
   16777217         18 db file sequential read                 1 20:48 20:48      10.02
   16777217         63 ON CPU                                  3 20:48 20:49      30.09
   16777217         16 db file sequential read                 1 20:48 20:48      10.03

6 rows selected.

SQL> 

Bottom line: this query is related to a problem I had with an ORA-00600 a couple of weeks ago happenign during the query transformation phase of a hard parse. We did not release a fix for this, prefering to defer until the next major release for the application, because the ORA-00600 was not fatal.

However, whilst the ORA-00600 is not fatal to the end process it is very much a performance problem and the impact depends on how much work the session has done when it gets the error.

If we look in the alert log, we find the ORA-00600 in question along with some interesting timestamps that broadly correlate with the episodes of log file sequential read from the ASH output above:

Tue May 08 19:57:14 2012
Errors in file /......directory.../trace file.trc (incident=2356995):
ORA-00600: internal error code, arguments: [kkqtSetOp.1], [], [], [], [], [], [], [], [], [], [], []
...
Tue May 08 20:48:25 2012
Use ADRCI or Support Workbench to package the incident.
See Note 411.1 at My Oracle Support for error and packaging details.
Errors in file /......directory.../trace file.trc (incident=2356996):
ORA-00600: internal error code, arguments: [kkqtSetOp.1], [], [], [], [], [], [], [], [], [], [], []

If we look at the incident files in question, the second (incident=2356996) is a continuation of the first (incident=2356995).

So what are these log file sequential reads all about?

Following the ORA-00600, it’s diagnostic information – specifically the action “Dump Online Redo for Buffers in Pin History”.

In this case, the getting and dumping of these buffers took the best part of the 50 minutes and once done the query continued on its merry way and the process completed.

What we see in the incident file is a timeline like this starting at 19:57:18:

*** timestamp before redo dump: 05/08/2012 19:57:18
***********************************************
* Dump Online Redo for Buffers in Pin History *
***********************************************
$$$$$$$ Dump Online Redo for DBA list (tsn.rdba in hex):
  0xa.05dbf102  0xa.05dbf103  0xa.05dbf104  0xa.05dbf105  0xa.05dbf107  0xa.05dbf109  0xa.05dbf110  0xa.05dbf111  0xa.05dbf113  0xa.05dbf115  0xa.05dbf116  0xa.05dbf119  0xa.05dbf11b  0xa.05dbf11c  0xa.05dbf11e  0xa.05dbf11f:
DUMP REDO
 Opcodes *.*
 DBAs (file#, block#):
 (23, 1831170) (23, 1831171) (23, 1831172) (23, 1831173) (23, 1831175) (23, 1831177) (23, 1831184) (23, 1831185) (23, 1831187) (23, 1831189) (23, 1831190) (23, 1831193) (23, 1831195) (23, 1831196) (23, 1831198) (23, 1831199) .
 SCNs: scn: 0x0000.00000000 thru scn: 0xffff.ffffffff
 Times: 05/08/2012 18:57:18 thru eternity
Initial buffer sizes: read 1024K, overflow 832K, change 805K

*** 2012-05-08 19:57:34.263
Log read is SYNCHRONOUS though disk_asynch_io is enabled!
Log read is SYNCHRONOUS though disk_asynch_io is enabled!
Log read is SYNCHRONOUS though disk_asynch_io is enabled!
Log read is SYNCHRONOUS though disk_asynch_io is enabled!

*** 2012-05-08 19:57:57.137
 Thread 1 low checkpoint scn: 0x077b.9d30745d
 SCN Start Scan Point: scn: 0x077b.9d30745d (8227499570269)
Initial buffer sizes: read 1024K, overflow 832K, change 805K
 INCARNATION:
  START: scn: 0x077a.31629df7 (8221395951095) Timestamp:  04/02/2012 14:41:00
  END: scn: 0xffff.ffffffff

*** 2012-05-08 19:58:09.227
Log read is SYNCHRONOUS though disk_asynch_io is enabled!
 descrip:"Thread 0001, Seq# 0000018211, SCN 0x077b9d30745d-0x077b9d31adcd"
.....

Through to 20:48:25 when:
*** 2012-05-08 20:48:25.097
END OF DUMP REDO
***********************************************
* End Dumping Redo for Buffers in Pin History *
***********************************************
*** timestamp after redo dump: 05/08/2012 20:48:25
*** time cost of this redo dump is 3067 seconds

Credit to Coskan for his observations, asking the right questions and pointing me in the direction of this post by Jonathan Lewis, including the comments, particularly #12 from Timur Akhmadeev which pointed in the right direction.

This is now the second time in a matter of a few weeks that I’ve seen this dump behaviour itself cause serious knock-on issues.

The previous time was on one occasion during my ORA-00600 post-upgrade storm where this recursive getting and dumping of redo buffers – this time caused by an ORA-07445 – meant that certain allocated resources were not freed in a timely fashion resulting in a large queue for “library cache : mutex x” until the dumping session was killed.


Regexp hungry for CPU? Real time sql monitoring shows just how

Not exactly a snappy blog post title…

First up, an execution plan showing the old problem of how the costs of a scalar subquery are not properly accounted for, even in the latest 11gR2 releases.

The SQL looks something like this:

INSERT...
SELECT ...
,      (SELECT to_some_object(....) 
        FROM   XXX,YYY ... 
        WHERE  ...
        AND    INSTR(outer.something,YYY.something) >0
FROM   <outer>

The execution plan like so:
------------------------------------------------------------------------------------
| Id  | Operation                | Name    | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------
|   0 | INSERT STATEMENT         |         |     1 |   173 |   827K  (1)| 01:08:59 |
|   1 |  LOAD TABLE CONVENTIONAL | ABC     |       |       |            |          |
|*  2 |   COUNT STOPKEY          |         |       |       |            |          |
|   3 |    NESTED LOOPS          |         |     1 |    45 |    12   (0)| 00:00:01 |
|*  4 |     TABLE ACCESS FULL    | XXX     | 37535 |   879K|     2   (0)| 00:00:01 |
|   5 |     INLIST ITERATOR      |         |       |       |            |          |
|*  6 |      INDEX RANGE SCAN    | YYY_IDX |     1 |    21 |     5   (0)| 00:00:01 |
|   7 |   NESTED LOOPS           |         |       |       |            |          |
...................
                   
Predicate Information (identified by operation id):
---------------------------------------------------
                                                   
   2 - filter(ROWNUM=1)                            
   4 - filter(INSTR(:B1,"XXX"."INST_USER_CODE")<>0 AND "XXX"."INST_TYPE_CODE"='BOND' 
              AND "XXX"."ACTIVE_FLAG"='Y')
   6 - access(("YYY"."XCOD_CODE"='ACON' OR "YYY"."XCOD_CODE"='CUSP' 
               OR "YYY"."XCOD_CODE"='ISIN' OR "YYY"."XCOD_CODE"='SEDL') 
               AND "YYY"."INST_NUM"="XXX"."INST_NUM" 
               AND "YYY"."INST_CODE"="XXX"."INST_USER_CODE")
..................

There are two problems with a scalar subselect:
1. As mentioned, the costs are not properly accounted for and
2. The scalar subselect gives the optimizer no chance to unnest the subquery so for the FULL table scan, this could be really problematic.

But it’s only after a change from INSTR to a regular expression that we see:
1. Just how expensive this lack of an unnest might be and
2. Just how cpu hungry regular expressions can be.

Using real time sql monitoring to demonstrate… (I’ve had to brutally cut out the extra columns to keep the width within the boundaries of this layour)

Original with INSTR:

SQL ID                                 :  a85q079dshta4                   
Duration                               :  92s                             

Global Stats
====================================================
| Elapsed |   Cpu   |    IO    |  Other   | Buffer |
| Time(s) | Time(s) | Waits(s) | Waits(s) |  Gets  |
====================================================
|      92 |      92 |     0.22 |     0.02 |    12M |
====================================================

================================================================================================
| Id |      Operation            |  Name   |  Time   | Execs |  Rows  |Activity|Activity Detail|
|    |                           |         |Active(s)|       |(Actual)|  (%)   |  (# samples)  |
================================================================================================
|  0 | INSERT STATEMENT          |         |         |     1 |        |        |               |
|  1 |   LOAD TABLE CONVENTIONAL |         |      87 |     1 |      0 |        |               |
|  2 |    COUNT STOPKEY          |         |      87 |   480 |    480 |        |               |
|  3 |     NESTED LOOPS          |         |      87 |   480 |    480 |        |               |
|  4 |      TABLE ACCESS FULL    | XXX     |      91 |   480 |    542 |  97.83 | Cpu (90)      |
|  5 |      INLIST ITERATOR      |         |      87 |   542 |    480 |        |               |
|  6 |       INDEX RANGE SCAN    | YYY_IDX |      87 |  1671 |    480 |        |               |
|  7 |    NESTED LOOPS           |         |      87 |     1 |    481 |        |               |
...................

And then the modified version using regular expression (done to solve a problem with the original hence the very slight decrease in some of the rows in the rowsources)
Note:

  • SQL duration gone from 92 seconds to 2290 seconds
  • Both plans spend most of the time on Step 4

SQL ID                                 :  4d1yffvgrzyxv                   
Duration                               :  2302s                           

Global Stats
====================================================
| Elapsed |   Cpu   |    IO    |  Other   | Buffer |
| Time(s) | Time(s) | Waits(s) | Waits(s) |  Gets  |
====================================================
|    2302 |    2299 |     2.56 |     0.40 |    12M |
====================================================

================================================================================================
| Id |      Operation            |  Name   |  Time   | Execs |  Rows  |Activity|Activity Detail|
|    |                           |         |Active(s)|       |(Actual)|  (%)   |  (# samples)  |
================================================================================================
|  0 | INSERT STATEMENT          |         |         |     1 |        |        |               |
|  1 |   LOAD TABLE CONVENTIONAL |         |    2297 |     1 |      0 |        |               |
|  2 |    COUNT STOPKEY          |         |    2297 |   480 |    480 |        |               |
|  3 |     NESTED LOOPS          |         |    2297 |   480 |    480 |        |               |
|  4 |      TABLE ACCESS FULL    | XXX     |    2301 |   480 |    541 |  99.74 | Cpu (2290)    |
|  5 |      INLIST ITERATOR      |         |    2297 |   541 |    480 |        |               |
|  6 |       INDEX RANGE SCAN    | YYY_IDX |    2297 |  1666 |    480 |        |               |
|  7 |    NESTED LOOPS           |         |    2297 |     1 |    481 |        |               |
...................

I love real time sql monitoring, just wish the output included the predicates section of the execution plan.


Create helloworld.exe file in powershell


PS> Add-Type -outputtype consoleapplication -outputassembly helloworld.exe 'public class helloworld{public static void Main(){System.Console.WriteLine("hello world");}}'
PS> .\helloworld.exe
hello world

Probably my first .exe this century :-)

Another debugging story…

There is a saying "correlation is not causation" meaning - just because you observe A and then observe B, it does not mean that A causes B - even if every single time you observe A - you see B as a 'side effect'.

Here is a great story to back that up - a debugging session where the end users had determined that they could not send email more than 500 miles.  Read it and note how much work the end users had gone into 'proving' that email cannot be sent more than 500 miles...

I use a similar story when talking about bind variables and bind variable peeking.  In the story - I make the claim that "when ever it rains on a Monday morning - you have to restart the database in the afternoon to make it perform correctly".  That is - rain on Mondays implies poor performance, and the only fix is to reboot.

This conclusion was reached via empirical observation - every single time, *every single time*, it would rain on Monday mornings - the database would respond with really poor response times to certain critical queries in the afternoon.  Every single time, without failure.  It must be that rain affects database performance somehow.

But here is what really was happening.  The database in question was a database that was backed up using cold backups (DBA's were of the archaic type).  These cold backups took place Sunday nights - late at night.  That meant that every Monday morning all of the caches would be virtually empty.  No parsed SQL for the applications in particular would be present.  So - Monday morning was the morning of hard parses every single week.

Now, in the critical application - there was a query that went against some very skewed data.  99.9999% of the time - the queries would be very selective, they would return 1 or 2 rows.  The other times - they would be very non-selective returning much of the table - but these queries were rare.

Now, the main user of this application was a person that came in early most of the time - they would get into the office at 6am and start working.  They always used bind inputs that returned very few rows - hence the problem queries would hard parse and tend to use indexes.  When this happened - everyone was happy.

The other set of users - they came in later, usually around 9am or so.  They would run the non-selective query and were OK with the index performance.  It might not have been the *best* possible performance - but it worked OK for them.

So, what was the link to rain?  Well, the office where everyone worked was located in Washington DC - when it rains in DC it doesn't matter if you leave home at 6am - it'll been noon before you get to work.  If you leave at 7am - it'll be noon when you get to work.  If you leave at 8am - it'll still be noon before you get to work.  In short - it really didn't matter when you left for work - if you are driving into the city - it is going to take a long time.  So, when it rained, the person that did the selective queries would just hit the snooze button on the alarm and go back to sleep.  Not worth driving in yet.

However, the people that did the non-selective queries - they lived in an apartment across the street from the  office.  They were not affected by the rain.  They came in at 9am regardless.  Since they ran the non-selective queries and the cache was empty - they would hard parse the queries and result with full scan plans.  This group of people was in fact a little convinced that rain *helped* performance a bit - when it rained on Mondays - they saw slightly better performance in the morning.

When the person that slept in finally got to work - and ran the application - they would definitely notice the full scans and how slow the database was.  Since it was raining - and they had observed this hundreds of times before - they call the data center - tell them "It is Monday, it is raining, you know what to do", after the reboot, everything is running fast again.

But - did it have anything to do with rain - or was it something entirely different :)

Getting to the root cause (bind peeking issue) can lead you to an efficient, effective corrective action (perhaps query plan stability for a certain set of queries for example).

Going down the empirical path of "correlation must imply causation" will lead to never actually fixing the problem - and the invention of many myths...

All Things Oracle Webinar

Thanks to everyone who attended my webinar sponsored by Red Gate and All Things Oracle. My topic was "How to Gather SQL Resource Consumption Metrics in Oracle". The webinar recording has been posted at AllThingsOracle as of May 9 but I thought I'd also make the materials available here for anyone interested. In the zip file, you'll find a PDF of the presentation slides, several example reports (ASH, AWR, SQL Monitor) as well as several scripts I used and a text file containing all the demo queries and output.

I was also asked a couple of questions that I wanted to follow up on. First, someone asked about the use of dbms_sqltune. As it turns out, it does require a license for the Oracle Tuning Pack in order to be able to legally use that package. That means that SQL Monitor reports are off limits without that license. Sigh...

Another person asked about how to create a SQL Profile and I included a script I use that was created by Kerry Osborne called create_sql_profile_awr.sql. You can get that script and several others along with a great overview of SQL Profiles at Kerry's blog.

Again, thanks to everyone who attended and I hope you found it interesting and helpful. Thanks also to the folks at Red Gate and particularly James Murtaugh who leads the charge at AllThingsOracle.com. Keep your eyes on the site for upcoming webinars, great articles and more!

How Do You Moderate LinkedIn Discussion Forums?

These are my personal rules that I’ve been following moderating the public forums on LinkedIn. I’ve posted on that topic in the discussion on IOUG Exadata SIG forum. As I’m passing RAC SIG group to the next folks on the board (I’m the RAC SIG president until end of August) I needed to hand over [...]

Oradebug hanganalyze with a prelim connection and “ERROR: Can not perform hang analysis dump without a process state object and a session state object.”

Back in the (really) old days, systemstate dumps had to be used for diagnosing hangs and finding blockers of hung databases. Basically you just identified which resource your waiting sessions were waiting for and then scanned through the rest of the system state dump to see which session held that particular resource (note that over time the instrumentation and systemstate dumps have evolved to resolve and dump the blocker information right at the waiting session section in the dump).

Diagnosing complex hangs was a tedious manual process (or required some scripting), so the hanganalyze was a really welcome addition to hang diagnosis. The hanganalyze basically walked through the blocker-waiter chains, found which session was the LEAF in the chain (the LEAF is the ultimate/final blocker in the wait chain), which everybody either directly or indirectly was waiting for.

Additionally the hanganalyze output allows you to draw the topology of the hang too, which may be useful in complex hang scenarios. In fact Enterprise Manager’s hang topology page also just calls ORADEBUG HANGANALYZE internally and formats the output into a visual hang graph (this is why the hang page requires OS credentials or sysdba privileges).

Starting from 11g, there is a welcome change though – there’s a new V$WAIT_CHAINS view, which lists all the hangs it has detected, it’s RAC-aware and is accessible via plain SQL. No need to run oradebug anymore, assuming that you are able to log on to query that V$ view :-)

And this is where we have a little problem – in rare circumstances an instance can get hung so that even a SYSDBA privilege holder can not log on, so how would you run the diagnostic queries & commands? This is why Oracle has introduced the preliminary connection option in sqlplus for diagnosing hung databases. With a prelim connection you will have a server process started for you, it attaches to the SGA shared memory segments, but it skips the last step of setting up that session/process-related structures in SGA. These operations on shared SGA structures have to be protected by latches/mutexes and would potentially get blocked if the related latches/mutexes are already held by someone else part of that hang.

I have already written about How to log on to Oracle when even sysdba cannot do so, but here’s an example:

$ sqlplus -prelim "/as sysdba"

SQL*Plus: Release 11.2.0.3.0 Production on Sun Jan 29 12:48:03 2012

Copyright (c) 1982, 2011, Oracle.  All rights reserved.

SYS:dbm1> ORADEBUG HANGANALYZE 3

Statement processed.

The “problem” is though that prelim connections only allow you to run ORADEBUG commands as running regular SQL do require that all the session/process structures are set up properly, plus that the parsing, library cache access etc would get latches/mutexes which could again block.

But we just wanted to dump hang analysis and ORADEBUG HANGANALYZE above seems to have succeeded. However when looking into the resulting tracefile, we see this:

*** 2012-01-29 12:48:11.041
Processing Oradebug command 'HANGANALYZE 3'
===============================================================================
HANG ANALYSIS:

ERROR: Can not perform hang analysis dump without a process
       state object and a session state object.
  ( process=(nil), sess=(nil) )

===============================================================================

It looks like hanganalyze isn’t able to work anymore without a fully logged on session where the process & session state objects (in v$process and v$session respectively) have been set up. This is a bit weird as one of the benefits of hanganalyze (and systemstate dumps) was that it did not require allocating any SGA structures nor take any latches & locks. 

This behavior change seems to have appeared since Oracle 11.2.0.2 and it’s documented in MOS note 452358.1, but that note doesn’t offer a good alternative for diagnosing hangs in such situations.

The alternative is actually simple – you should attach to an existing fully-initialized & logged on process with oradebug and let that process do the hanganalyze for you. Whatever process you attach to with oradebug is the process which will be performing the work for you.

So if you ever see the error above when diagnosing extreme hangs, then just identify some least critical process logged in to the database (not a background process ideally and definitely not critical bg processes such DBWR & LGWR etc) and attach to that with oradebug (as opposed to “oradebug setmypid”) and the hang analysis should work ok.

However, the catch is that the above ORADEBUG HANGANALYZE command is a shortcut, which always tries to run hanganalyze in your (preliminary) connection, by calling the hanganalyze function directly in the process. So you have to use ORADEBUG DUMP HANGANALYZE 3 instead (the 3 is the detail level where I usually start from) as this syntax will send the “dump hanganalyze” command to the target (fully-initialized) process where you’ve attached to.

Update: I added this to clarify which commands should you use when you hit this problem:

  1. sqlplus -prelim “/as sysdba”
  2. ORADEBUG SETOSPID OS_PID
  3. ORADEBUG DUMP HANGANALYZE 3

Enjoy :)

Toad 11.5 is out

The latest Toad is now in production, 11.5, get it from http://toadfororacle.com.

If you have an old license key, 9.6 or older, it may complain at installation time, just ignore. It will be fine at run time.

Enhanced TAB browsing experience, nicer and more visible colors for your connection (production=red…), read-only connections.

Currently it still requires a 32bit clients, even when running on a 64bit Operating System.