Initial setup:
PLSHPROF utility:
Reference
- Oracle 11g Advanced application developers guide
- Oracle 11g PLSQL packages and types reference.
- Www.oracle-base.com
 DBMS_UTILITY.GET_DEPENDENCY  
 type IN VARCHAR2,  
 schema IN VARCHAR2,  
 name IN VARCHAR2);   set serveroutput on;  
 BEGIN  
 dbms_utility.get_dependency('TABLE','HR','EMPLOYEES');  
 END;   DEPENDENCIES ON HR.EMPLOYEES  
 ------------------------------------------------------------------  
 *TABLE HR.EMPLOYEES()  
 *  VIEW HR.EMP_DETAILS_VIEW()  
 *  TRIGGER HR.SECURE_EMPLOYEES()  
 *  TRIGGER HR.UPDATE_JOB_HISTORY()  
 *  PROCEDURE HR.ANNUAL_SALARY()       
 DBMS_UTILITY.COMPILE_SCHEMA (  
 schema IN VARCHAR2,  
 compile_all IN BOOLEAN DEFAULT TRUE,  
 reuse_settings IN BOOLEAN DEFAULT FALSE);  
 BEGIN  
 DBMS_UTILITY.COMPILE_SCHEMA('HR',FALSE,TRUE);  
 END;  
DBMS_PROFILER package, introduced in Oracle8i, provides a means to collect profiling data of plsql program units for performance improvement.
To use dbms_profiler package, we need to create the database tables which will store the profiler data. These tables can be created by running proftab.sql script which will be present in /rdbms/admin folder in the oracle installation folder.
This script creates:
Lets do the initial setup:
1: CONN SYS/PASSWORD AS SYSDBA
2: @/home/oracle/app/oracle/product/11.2.0/dbhome_2/rdbms/admin/proftab.sql
3: CREATE PUBLIC SYNONYM plsql_profiler_runs FOR plsql_profiler_runs;
4: CREATE PUBLIC SYNONYM plsql_profiler_units FOR plsql_profiler_units;
5: CREATE PUBLIC SYNONYM plsql_profiler_data FOR plsql_profiler_data;
6: CREATE PUBLIC SYNONYM plsql_profiler_runnumber FOR plsql_profiler_runnumber;
7: GRANT SELECT ON plsql_profiler_runnumber TO PUBLIC;
8: GRANT SELECT, INSERT, UPDATE, DELETE ON plsql_profiler_data TO PUBLIC;
9: GRANT SELECT, INSERT, UPDATE, DELETE ON plsql_profiler_units TO PUBLIC;
10: GRANT SELECT, INSERT, UPDATE, DELETE ON plsql_profiler_runs TO PUBLIC;
The sets involved in profiling a plsql unit using dbms_profiling is: 
An Example: 
1: create or replace procedure annual_salary
2: as
   3:  l_annual_salary NUMBER; 4: begin
5: for i in ( select employee_id, salary from employees)
   6:  loop    7:  l_annual_salary := i.salary * 12; 8: end loop;
9: end annual_salary;
  10:   /11: DECLARE
  12:    l_result  BINARY_INTEGER; 13: BEGIN
14: l_result := DBMS_PROFILER.start_profiler(run_comment => 'annual_salary' );
  15:    annual_salary;   16:    l_result := DBMS_PROFILER.stop_profiler; 17: END;
  18:  / Run this query
SELECT runid,  run_date,  run_comment,  run_total_time FROM   plsql_profiler_runs where run_comment = 'annual_salary'
ORDER BY runid;
This gave below output: 
To analyze which subprogram or line in the plsql unit profiled took how much time :
SELECT u.runid, u.unit_number, u.unit_type, u.unit_owner, u.unit_name, d.line#, d.total_occur,d.total_time, d.min_time, d.max_time
FROM plsql_profiler_units u JOIN plsql_profiler_data d ON u.runid = d.runid
AND u.unit_number = d.unit_numberWHERE u.runid = 4 ORDER BY u.unit_number, d.line#;
This produces below output: 
The lines 6 and 8 has taken maximun time.
Using dbms_profiler, one can tune their plsql units iteratively. 
Regards, 
Vijay